【C# Task】开篇


概览

在学task类之前必须学习线程的知识。

以下是task命名空间的类的结构图

1、2种任务类型: 有返回值task 、无返回值task。

2、2座任务工厂 TaskFactory/TaskFactory

3、2种

4、3种类型的

5、

6、8个

7、

8、取消任务 CanellationTokenSource

9、任务并行

10、异步任务 async/await

创建Task

任务的初始化配置 :

1、构造函数new Task 创建任务

  1. Task(Action) 
  2. Task(Action, CancellationToken)
  3. Task(Action, TaskCreationOptions)
  4. Task(Action, Object) 
  5. Task(Action, CancellationToken, TaskCreationOptions)  
  6. Task(Action, Object, CancellationToken) 
  7. Task(Action, Object, TaskCreationOptions) 
  8. Task(Action, Object, CancellationToken, TaskCreationOptions)

    (1)默认start()方法调用TaskScheduler.Current 任务调度器。如果要指定调度器请使用Start(TaskScheduler)TaskScheduler.Current 是task默认的任务调度器,因为任务内部会创建子任务,如果是ui的调度器 就利用消息机制,如果是线程池就用线程池任务调度器。

    Task()创建的任务处于 Created状态,必须调用Start运行任务后任务状态才能变成WaitingToRun。

    //相当于:Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); task状态为WaitingForRun。

    而默认的TaskScheduler采用的是.NET线程池ThreadPool,它主要面向的是细粒度的小任务,其执行时间通常在毫秒级。

    2、Task.Run方式创建task

    Task.Run已经完成配置,直接使用。它的默认配置是:Task.Factory.StartNew(someAction , CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

    TaskFactory.StartNew(工厂创建)/Task.Run() 方法是用于创建和计划计算的任务的首选的机制。该创建的任务处于WaitingToRun状态(注意:异步任务总是 WaitingForActivation)。Task.Run()轻型的TaskFactory.StartNew。

    注意:TPL ( Task-Parallel-Library )、Task类(class)和 TaskStatus枚举是在 async-await 关键字引入之前就已经存在了。
    async-await 引入后任务会多一种状态,但是又不能去修改之前发布的TaskStatus枚举。所以结合异步的基本处于等待状态的特性。
    就将异步任务的运行状态表示为WaitingForActivation状态。

    var task1 = new Task(() =>
        {
           //相当于:Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); 状态为WaitingForRun。
        });
       task1.Start();
    ///异步总是 WaitingForActivation   
    //详细解释 https://stackoverflow.com/questions/20830998/async-always-waitingforactivation
    var t = Task.Run(async delegate
            {
                await Task.Delay(5000);
                return 42;
            });
            Console.WriteLine(t.Status);///异步总是 WaitingForActivation
            t.Wait();
            Console.WriteLine(t.Status);
            Console.WriteLine("Task t Status: {0}, Result: {1}",
                              t.Status, t.Result);

    3、Task.ContinueWith 创建后续任务

     这种方式创建的任务,依附于主任务。这种方式创建的任务处于WaitingForActivation状态。Task.ContinueWith()内部用默认的TaskScheduler.Current,触发显示的指定任务计划否则都用默认的。

    Task main = Task.Run(
        () => {
            Console.WriteLine($"{Environment.CurrentManagedThreadId}  running");
           Thread.Sleep(2000);
        }
    
    ) ;
    Task  task1=main.ContinueWith(ts => Console.WriteLine("sub task ")) ;
    Console.WriteLine(task1.Status);
    //输出 WaitingForActivation

    4、工厂方式创建Task

    (1)、有两座工厂一个是泛型工厂TaskFactory()和普通工厂TaskFactory() ,泛型工厂有任务返回值,返回值保存在任务的Result。

    用工厂模式创建任务第一步就是配置工厂设置,两座工厂的配置都是一样的,也可采用默认的设置。

        TaskFactory() 采用默认模式工厂设置。默认工厂设置TaskCreationOptions.None,TaskContinuationOptions.None ,CancellationToken.None, TaskScheduler.Scheduler=null 创建任务时候使用TaskScheduler.Current。

        TaskFactory(CancellationToken)单独设定一个CancellationToken,其他配置采用默认工厂设置

        TaskFactory(TaskScheduler)  单独设定一个任务调度器,其他配置采用默认工厂设置

        TaskFactory(TaskCreationOptions, TaskContinuationOptions),其他配置采用默认工厂设置

        TaskFactory(CancellationToken, TaskCreationOptions, TaskContinuationOptions, TaskScheduler)全部重新设置

    (2)、利用默认配置或者重新配置过的设置,创建任务。

    (3)、Task.Factory.StartNew() 采用的是 默认的工厂设置。

    1var task = Task.Run(() => Console.WriteLine("cancelled"));
    2var task2 = Task.Factory.StartNew(() =>
    {
        //TODO you code
    });
    3var t = Task<int>.Run(() => {
        // Just loop.
        int max = 1000000;
        int ctr = 0;
        for (ctr = 0; ctr <= max; ctr++)
        {
            if (ctr == max / 2 && DateTime.Now.Hour <= 12)
            {
                ctr++;
                break;
            }
        }
        return ctr;
    });
    
    Console.WriteLine("Finished {0:N0} iterations.", t.Result);

    Task的任务控制

    老的新的描述
    task.Wait await task 等待/等待任务完成
    task.Result await task 获取完成任务的结果
    Task.WaitAny await Task.WhenAny 等待/等待一组任务中的一个完成
    Task.WaitAll await Task.WhenAll 等待/等待一组任务中的每一个完成
    Thread.Sleep await Task.Delay 等待/等待一段时间
    Task constructor Task.Run or TaskFactory.StartNew 创建基于代码的任务

     Wait         阻塞
    WaitAll     返回bool
    WaitAny   返回任务数组的第一完成任务索引(包括取消、错误、完成)
    WhenAll   返回数组或者集合任务结果(包括取消、错误、完成)
    WhenAny 返回数组或者集合任务第一完成任务(包括取消、错误、完成)

    Run/start 运行任务 

       Run():静态方法线程池线程运行
       RunSynchronously()/RunSynchronously(TaskScheduler)实例方法当前线程运行,可以传入任务调度器,或者采用默认线程池任务调度器.通过调用 RunSynchronously() 方法执行的任务必须是 Task 或 Task 类构造函数进行实例化。 要同步运行的任务必须处于 Created 状态。 任务只能启动并运行一次。 如果尝试再次计划任务,将导致异常。Task()创建的任务处于 Created状态,必须调用Start运行任务后任务状态才能变成WaitingtoRun。

       Start ()/Start(TaskScheduler):实例方法 运行任务,可以传入任务调度器,或者采用默认线程池任务调度器。通过调用Start()方法执行的任务必须是 Task 或 Task 类构造函数进行实例化。 要同步运行的任务必须处于 Created 状态。 任务只能启动并运行一次。 如果尝试再次计划任务,将导致异常。Task()创建的任务处于 Created状态,必须调用Start运行任务后任务状态才能变成WaitingtoRun。

    //在当前线程运行任务
    Console.WriteLine("app threadID:" + Environment.CurrentManagedThreadId);
    Task tasksync = new Task(() => Console.WriteLine($"taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}")) ;
    tasksync.RunSynchronously();
    
    
    //采用默认的线程池线程
    Task task = new(()=> Console.WriteLine($"taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}"));
    task.Start();
    
    //采用默认的线程池线程
    Task.Run(() => Console.WriteLine($"taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}"));
    Console.Read();

    Task.Delay延迟任务执行

    Delay(Int32, CancellationToken) 创建一个在指定的毫秒数后完成的可取消任务。
    Delay(TimeSpan, CancellationToken) 创建一个在指定的时间间隔后完成的可取消任务。
    Delay(Int32) 创建一个在指定的毫秒数后完成的任务。
    Delay(TimeSpan) 创建一个在指定的时间间隔后完成的任务。

    1.Task.Delay实质是创建一个任务,再任务中开启一个定时间,然后延时指定的时间
    2.Task.Delay不和await一起使用情况,当代码遇到Task.Delay一句时,创建了了一个新的任务去执行延时去了,当前代码继续往下执行,这情况task.wait 是不包含Task.Delay的时间的。
    3.Task.Delay和await一起使用,当代码遇到await Task.Delay时候,当前线程要等该行代码执行完成后,再继续执行后面的代码

    //同步阻塞
    Task task = new(  ()=> {
      Task.Delay(5000).Wait();//同步阻塞 只有直接到了,才会继续执行后面的代码。

    //不阻 创建了一个新的任务去执行延时去了,当前代码继续往下执行
    Task.Delay(5000);
    Console.WriteLine($
    "taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}"); }); //异步阻塞 Task task2 = new(async () => { await Task.Delay(5000) ; Console.WriteLine($"taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}"); // 不会等待异步或者同步的Task.Delay方法,也可说Wait没达到预期效果。 }); task.Start();//task.Wait();达到预期效果 task2.Start();//task.Wait();不能达到预期效果 Console.ReadLine();

    Task.Delay(),Task.Delay是基于计时器的等待机制,使用系统计时器,系统定时器的滴答速度约为16ms(windows 为15ms)。这意味着,如果参数msecondsdelay小于系统时钟的分辨率(在Windows系统上大约为15毫秒),那么时间延迟将大约等于系统时钟的分辨率。如果查看源代码,您会找到对Timer类的引用,该类负责延迟。另一方面,Thread.Sleep实际上使当前线程进入休眠状态,这样你只是阻塞并浪费一个线程。在异步编程模型中,如果您希望在延迟一段时间后发生某些事情(延续),则应始终使用Task.Delay()。await Task.Delay()释放线程做其他事情,直到计时器到期,100%清除。

    使用Task.Delay,您始终可以提供取消令牌并优雅地将其删除。这就是我选择Task.Delay的一个原因。

    异步代码的一个主要优点是允许一个线程同时处理多个任务,避免阻塞调用。这避免了对大量单个线程的需求,并允许线程池一次为多个请求提供服务。但是,鉴于异步代码通常在线程池上运行,不必要地使用Thread.Sleep()阻塞单个线程会占用整个线程,否则可能会在其他地方使用。如果使用Thread.Sleep()运行许多任务,则很有可能耗尽所有线程池线程并严重阻碍性能。在线程池线程中运行Thread.Sleep()是一个不好的做法。

    //使用方式一,在异步中使用
    public static Task ShortDelay(TimeSpan delay)
    {
        await Task.Delay(delay);
        Console.WriteLine(string.Format("延迟{0}", delay));
    }
    //使用方式二,在同步代码中使用,没任何效果
    Task.Factory.StartNew(() =>
                {
                    Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") +  " ====== 开始Delay()");
                    for (int i = 101; i < 120; i++)
                    {
                       Task ass= Task.Delay(5000);
                        ass.ContinueWith(t => Console.WriteLine("fdfdf"));
                        Console.WriteLine(ass.Id);
                        Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ===Delay=== " + i);
                        Task.Delay(100);//fang'hui
    
    
                    }
                    Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ====== 结束Delay()");
                });

    C#中的Task.Delay()和Thread.Sleep() 

    1. Thread.Sleep()是同步延迟,Task.Delay()是异步延迟。
    2. Thread.Sleep()会阻塞线程,Task.Delay()不会。
    3. Thread.Sleep()不能取消,Task.Delay()可以。
    4. Task.Delay()实质创建一个运行给定时间的任务,Thread.Sleep()使当前线程休眠给定时间。
    5. 反编译Task.Delay(),基本上讲它就是个包裹在任务中的定时器。
    6. 4. Task.Delay() 比 Thread.Sleep() 消耗更多的资源,但是Task.Delay()可用于为方法返回Task类型;或者根据CancellationToken取消标记动态取消等待
    7. Task.Delay()和Thread.Sleep()最大的区别是Task.Delay()旨在异步运行,在同步代码中使用Task.Delay()是没有意义的;在异步代码中使用Thread.Sleep()是一个非常糟糕的主意。通常使用await关键字调用Task.Delay()。
    8. 我的理解:Task.Delay(),async/await和CancellationTokenSource组合起来使用可以实现可控制的异步延迟

    同步等待任务,会阻塞线程

    wait

    等待一个任务完成,都是实例方法

    void Wait()

    无限等待  Task 完成执行过程。
    【异常】 1、任务被释放 2、任务被取消 3、任务内部抛出异常

    bool Wait(Int32) 

    【参数】Int32 等待 Task 在指定的毫秒数内完成执行,或为 Infinite (-1),表示无限期等待。 超时返回false
    【异常】1、任务被释放 2、参数Int32超出范围int.MaxValue>Time>=-1  3、任务内部抛出异常 4、任务被取消


    bool Wait(Int32, CancellationToken)    

    【参数】Int32 等待 Task 完成执行过程。 如果在任务完成之前超时间隔结束或取消标记已取消,等待将终止,或为 Infinite (-1),表示无限期等待。。超时返回false
    【异常】1、任务被释放 2、任务被取消 3、任务内部抛出异常  4、Wait(CancellationToken)  CancellationToken被取消 5、参数Int32超出范围int.MaxValue>Time>=-1

    bool Wait(TimeSpan)
    【参数】TimeSpan在指定的时间间隔内完成任务才会返回true,否则放回false。表示等待的毫秒数的TimeSpan,或者表示-1毫秒的TimeSpan,表示无限期等待。内部转化成Wait(Int32, CancellationToken)等待 。超时返回false
    【异常】1、任务被释放 2、任务被取消 3、任务内部抛出异常   4、参数TimeSpan超出范围int.MaxValue>Time>=-1

    void Wait(CancellationToken)

     【参数】CancellationTokenTask 完成执行过程 无限等待直到取消  。 
    【异常】1、任务被释放 2、任务被取消 3、任务内部抛出异常  4、Wait(CancellationToken)  CancellationToken被取消

    void Wait() 案例

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;
    Task task4 = Task.Run(
        () => {
            Console.WriteLine("任务4开始");
            while (true)
            {
             
                if (ct.IsCancellationRequested)
                {
                 
                    ct.ThrowIfCancellationRequested();
                }
                Task.Delay(2000).Wait();
            }
               
    
        }, cts.Token
        );
    
    cts.Cancel();
    
    try {
      //  1、任务被释放 2、任务被取消 3、任务内部抛出异常
        task4.Wait();
    }
    catch (Exception ex) { Console.WriteLine(ex.Message); }
    
    Console.Read();

    void Wait(CancellationToken)案例

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;
    Task task4 = Task.Run(
        () => {
            Console.WriteLine("任务4开始");
            while (true)
            {
                if (ct.IsCancellationRequested)
                {
                 
                    ct.ThrowIfCancellationRequested();
                }
                Task.Delay(2000).Wait();
            }        
        }
        );
    
    cts.Cancel();
    
    try {
        //  1、任务被释放 2、任务被取消 3、任务内部抛出异常  4、Wait(CancellationToken)  触发取消
        task4.Wait(ct);
    }
    catch (Exception ex) { Console.WriteLine(ex.Message); }
    
    Console.Read();

    public bool Wait (int millisecondsTimeout)案例

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;
    Task task4 = Task.Run(
        () => {
            Console.WriteLine("任务4开始");
            while (true)
            {
             
                if (ct.IsCancellationRequested)
                {
                 
                    ct.ThrowIfCancellationRequested();
                }
                Task.Delay(2000).Wait();
            }
               
    
        },ct
        );
    
    //cts.Cancel();
    
    try {
      //  1、任务被释放 2、时间超出范围  3、任务内部抛出异常 4、任务被取消
        task4.Wait(100);//超时返回false
    }
    catch (Exception ex) { Console.WriteLine(ex.Message); }
    
    Console.Read();

    WaitAll

    等待一组任务完成,都是静态方法

    public static void WaitAll (params  Task[] tasks)
    【异常】 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素  4、任务被取消了 5、任务内部抛出异常

    public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
    【参数】int millisecondsTimeout 表示所有的数组中的任务在规定的 时间内完成 才返回true,否则返回false。  Infinite (-1),表示无限期等待。超时返回false
    【异常】1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素  4、任务被取消了 5、任务内部抛出异常 6、参数millisecondsTimeout 超出范围int.MaxValue>Time>=-1

    [System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
    public static void WaitAll (Task[] tasks,  CancellationToken cancellationToken)
    【特性】UnsupportedOSPlatform("browser"):表示该api不被浏览器支持。
    【异常】 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常  6、WaitAll(arrtasks, ct)方法ct 取消


    public static bool WaitAll ( Task[] tasks, TimeSpan timeout)
    【参数】TimeSpan timeout:在时间间隔内等待(例如:TimeSpan.FromMilliseconds(1000)  表示 1s)  或者表示-1毫秒的TimeSpan,表示无限期等待。 内部也是转化成public static bool WaitAll (Task[] tasks, int millisecondsTimeout)。超时 返回后false。
    【异常】 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常  6、参数timeout 超出范围int.MaxValue>Time>=-1


    [System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
    public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken);
    【特性】UnsupportedOSPlatform("browser"):表示该api不被浏览器支持。
    【参数】int millisecondsTimeout 所有的数组中的任务在规定的 时间内完成 才返回true,否则返回false。  Infinite (-1),表示无限期等待。
    【异常】 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常 6、参数millisecondsTimeout超出范围int.MaxValue>Time>=-1 7、参数cancellationToken取消


    public static void WaitAll (params  Task[] tasks)案例

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;
    
    Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成");   });
    Task task2 = Task.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); });
    Task task3 = Task.Run(() => { Console.WriteLine("任务3开始"); Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); });
    Task task4 = Task.Run(
        () => {
            Console.WriteLine("任务4开始");
            while (true)
            {
    
                if (ct.IsCancellationRequested)
                {
                    ct.ThrowIfCancellationRequested();
                }
                Task.Delay(2000).Wait();
            }
               
    
        }, cts.Token
        );
    
    cts.Cancel();
    
    Task[] arrtasks = new[] { task1, task2, task4 };
    try {
         
        // 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素  4、任务被取消了 5、任务内部抛出异常
        bool dd =Task.WaitAll(arrtasks);//写法二 Task.WaitAll(task1, task2, task3); 
        //
        Console.WriteLine(dd);
    }catch (Exception ex) { }
    
    Console.Read();

    public static bool WaitAll (Task[] tasks, int millisecondsTimeout)案例

    //其他代码同上
    try {
         
        // 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素  4、任务被取消了 5、任务内部抛出异常 6、时间超出范围
        bool waitStatus =Task.WaitAll(arrtasks, 20000);//wait超时 返回后false。
        Console.WriteLine(waitStatus);
    }catch (Exception ex) { }

    public static void WaitAll (Task[] tasks,  CancellationToken cancellationToken)案例

    //其他代码同上
    try {
    
        // 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素
        // 4、任务被取消了 5、任务内部抛出异常  6、WaitAll(arrtasks, ct)方法ct 取消
        Task.WaitAll(arrtasks, ct);
    
    }
    catch (Exception ex) { }

     
    public static bool WaitAll ( Task[] tasks, TimeSpan timeout)案例

    //其他代码同上
    
    Task[] arrtasks = new[] { task1, task2, task4 };
    TimeSpan ts = DateTime.Now - DateTime.Now.AddSeconds(-1);
    try {
    
        // 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素
        // 4、任务被取消了 5、任务内部抛出异常  6、时间超出范围X<-1 或 X>maxTime
    
      bool outime=  Task.WaitAll(arrtasks, ts);// Task.WaitAll(arrtasks, TimeSpan.FromMilliseconds(5000));//表示5s
    
        // 超时  返回后false。
        Console.WriteLine(outime );
    }
    catch (Exception ex) { }

    public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)案例

        //其他代码同上
    //cts.Cancel();
    
    Task[] arrtasks = new[] { task1, task2, task4 };
    TimeSpan ts = DateTime.Now - DateTime.Now.AddSeconds(-1);
    try {
    
        // 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素
        // 4、任务被取消了 5、任务内部抛出异常  6、时间超出范围X<-1 或 X>maxTime 7、WaitAll(arrtasks,1000, ct)方法ct 取消
    
        bool outime =  Task.WaitAll(arrtasks, 1000,ct);// Task.WaitAll(arrtasks, TimeSpan.FromMilliseconds(5000));//表示5s
    
        // 超时  返回后false。
        Console.WriteLine(outime );
    }
    catch (Exception ex) { }
    
    Console.Read();

    WaitAny

    等等待一组任务中 任意一个完成 返回值是数组索引。都是静态方法

    static int WaitAny(Task[], TimeSpan)
    【说明】等待任何提供的 Task 对象在指定的时间间隔内完成执行。任务成功返回数组索引
    【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、WaitAny(Task[], Int32)Int32 超出范围int.MaxValue>Time>=-1

    static int WaitAny(Task[], Int32, CancellationToken)   
    【说明】  等待提供的任何 Task 对象在指定的毫秒数内完成执行,或等到取消标记取消。Int32表示Task 对象在指定的毫秒数内完成执行,或表示 -1 毫秒(无限期等待) 。任务成功返回数组索引
    【异常】【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、WaitAny(Task[], Int32)Int32 超出范围int.MaxValue>Time>=-1 5、参数CancellationToken 取消


    static int  WaitAny(Task[], CancellationToken)
    【说明】 等待提供的任何 Task 对象完成执行过程(除非取消等待)。任务成功返回数组索引
    【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、参数CancellationToken 取消

    static int WaitAny(Task[], Int32)
    【说明】等待任何提供的 Task 对象在指定的毫秒数内完成执行或表示 -1 毫秒(无限期等待)。任务成功返回数组索引
    【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、WaitAny(Task[], Int32)Int32 超出范围int.MaxValue>Time>=-1

    static int WaitAny(Task[])   
    【说明】等待提供的任一 Task 对象完成执行过程。任务成功返回数组索引
    【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素


    static int WaitAny(Task[], Int32)案例,其他api就不举子了,都一样

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;
    
    Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); });
    Task task2 = Task.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); });
    Task task3 = Task.Run(() => { Console.WriteLine("任务3开始"); Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); });
    Task task4 = Task.Run(
        () => {
            Console.WriteLine("任务4开始");
            while (true)
            {
    
                if (ct.IsCancellationRequested)
                {
                    ct.ThrowIfCancellationRequested();
                }
                Task.Delay(2000).Wait();
            }
    
    
        }, cts.Token
        );
    
    //cts.Cancel();
    
    Task[] arrtasks = new[] { task1, task2, task4 };
    try
    {
    
        // 捕获异常 1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、WaitAny(Task[], Int32)Int32 超出范围int.MaxValue>Time>=-1 
       int index = Task.WaitAny(arrtasks,20000);//
        Console.WriteLine("数组索引"+index);
    }
    catch (Exception ex) { }
    
    Console.Read();



    异步等待任务,不会阻塞线程

    WaitAsync

    异步等待,实例方法,未提供int32类型Api但是可以用TimeSpan.FromMilliseconds(5000)来实现int32类型的等待。

    Task WaitAsync(TimeSpan, CancellationToken) 获取一个任务,该任务将在此任务完成时、指定的超时超时时或指定的CancellationToken被请求取消时完成。

     CancellationTokenSource CancellationTokenSource=new CancellationTokenSource();
    CancellationTokenSource.Cancel();
    Task<string> task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(4000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; });
    
    //异步等待2或者接收到取消信号,然后输出等待的状态
    task1.WaitAsync(TimeSpan.FromMilliseconds(2000),CancellationTokenSource.Token).ContinueWith((t) => Console.WriteLine("等待任务的状态"+t.Status));
    
    // 时间间隔内等待,等待5秒钟
    //TimeSpan ts = DateTime.Now - DateTime.Now.AddSeconds(-5);
    //task1.WaitAsync(TimeSpan.FromMilliseconds(ts)).ContinueWith((t) => Console.WriteLine("等待任务的状态" + t.Status));
    
    
    Console.WriteLine("任务1的状态" + task1.Status);
    Console.Read();
    /*输出
    任务1开始
    任务1的状态Running
    等待任务的状态Canceled
    任务1完成
    */


    Task WaitAsync(TimeSpan) 返回一个任务A,该任务将在B任务完成后 或指定超时超时时完成的任务。

    Task<string> task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(4000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; });
    
    //异步等待2s,然后输出等待的状态   未提供int32类型Api但是可以用TimeSpan.FromMilliseconds(5000)来实现int32类型的等待。
    task1.WaitAsync(TimeSpan.FromMilliseconds(2000)).ContinueWith((t) => Console.WriteLine("等待任务的状态"+t.Status));
    
    // 时间间隔内等待,等待5秒钟
    //TimeSpan ts = DateTime.Now - DateTime.Now.AddSeconds(-5);
    //task1.WaitAsync(TimeSpan.FromMilliseconds(ts)).ContinueWith((t) => Console.WriteLine("等待任务的状态" + t.Status));
    
    
    Console.WriteLine("任务1的状态" + task1.Status);
    Console.Read();
    /*输出
    任务1开始
    任务1的状态Running
    等待任务的状态Faulted
    任务1完成
    */


    Task WaitAsync(CancellationToken) 返回一个任务A,该任务将在B任务完成后完成

    WhenAll

    等待一组任务完成,返回值是task,都是静态方法

    static WhenAll(IEnumerable)  

     创建一个任务,该任务将在可枚举集合中的所有task对象都完成时完成。
    调用WhenAll(IEnumerable方法不会阻塞调用线程。
    如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
    如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
    如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
    如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。


    static WhenAll(paramsTask[])     

    创建一个任务,该任务将在数组中的所有task对象都完成时完成。
    调用WhenAll(Task[])方法不会阻塞调用线程。
    如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
    如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
    如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
    如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。


    static Task WhenAll(IEnumerable>)     

    创建一个任务,该任务将在可枚举集合中的所有task 对象都完成时完成。

    调用WhenAll(IEnumerable>)方法不会阻塞调用线程。但是,对返回的Result属性的调用会阻塞调用线程。
    如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
    如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
    如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。任务< TResult >。返回任务的Result属性将被设置为一个数组,其中包含所提供任务的所有结果,顺序与提供的顺序相同(例如,如果输入任务数组包含t1, t2, t3,则输出任务的task 。Result属性将返回一个TResult[],其中arr[0] == t1。结果,arr[1] == t2。和arr[2] == t3.Result)。
    如果tasks参数不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。返回的TResult[]将是一个包含0个元素的数组。


    static Task  WhenAll(paramsTask[])     

    创建一个任务,该任务将在数组中的所有task 对象都完成时完成。

    以上案例如下:

    static WhenAll(IEnumerable)案例

     
     var tasklist=new List();
    
    Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成");  });
    Task task2 = Task.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); });
    Task task3 = Task.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); });
    
    tasklist.Add(task1);
    tasklist.Add(task2);
    tasklist.Add(task3);
     
    Task taskswait = Task.WhenAll(tasklist);
    try
    {
        //等待期间可能发送错误、取消等 所有要捕获异常
        taskswait.Wait();
    }
    catch (OperationCanceledException ex)
    {
        Console.WriteLine(ex.Message);
    }

     注意
    调用WhenAll(IEnumerable方法不会阻塞调用线程。
    如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
    如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
    如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
    如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。

    static WhenAll(paramsTask[]) 案例

    Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); });
    Task task2 = Task.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); });
    Task task3 = Task.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); });
    
    Task[] arrtasks = new[] { task1 , task2 , task3 };
    
       Task taskswait= Task.WhenAll(arrtasks); 
    try
    {
        //等待期间 任务可能取消、发生异常等,所有要捕获异常
        taskswait.Wait();
    }
    catch (OperationCanceledException ex)
    {
        Console.WriteLine(ex.Message);
    }
    
    Console.Read();

     注意
    调用WhenAll(Task[])方法不会阻塞调用线程。
    如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
    如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
    如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
    如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。

     WhenAll(IEnumerable>)案例

     
     var tasklist=new Liststring>>();
    
    Task<string> task1 = Task<string>.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; });
    Task<string> task2 = Task<string>.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); return "task2complet"; });
    Task<string> task3 = Task<string>.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); return "task3complet"; });
    
    tasklist.Add(task1);
    tasklist.Add(task2);
    tasklist.Add(task3);
     
    Task<string[]>taskswait = Task<string>.WhenAll<string>(tasklist);
    try
    {
        //等待期间可能发送错误、取消等 所有要捕获异常
        taskswait.Wait();
    }
    catch (OperationCanceledException ex)
    {
        Console.WriteLine(ex.Message);
    }
    
    if (taskswait.Status == TaskStatus.RanToCompletion)
    {
        foreach (var item in taskswait.Result)
        {
            Console.WriteLine(item);
        }
    }
    else
    {
    
    
    }
    Console.Read();

     注意
    调用WhenAll(IEnumerable>)方法不会阻塞调用线程。但是,对返回的Result属性的调用会阻塞调用线程。
    如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
    如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
    如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。任务< TResult >。返回任务的Result属性将被设置为一个数组,其中包含所提供任务的所有结果,顺序与提供的顺序相同(例如,如果输入任务数组包含t1, t2, t3,则输出任务的task 。Result属性将返回一个TResult[],其中arr[0] == t1。结果,arr[1] == t2。和arr[2] == t3.Result)。
    如果tasks参数不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。返回的TResult[]将是一个包含0个元素的数组。

     参考案例:https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenall?view=net-6.0#system-threading-tasks-task-whenall-1(system-threading-tasks-task((-0))())

    WhenAny

    等待一组任务完成,返回值是Task>,Task是第一返任务。都是静态方法

    static Task WhenAny(IEnumerable)  
    创建一个任务,该任务将在任何提供的任务完成时完成。
    当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“已故障”状态结束,也同样如此。


    static Task WhenAny(Task[])  
    创建一个任务,该任务将在任何提供的任务完成时完成。
    当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“已故障”状态结束,也同样如此。

    static Task WhenAny(Task[])
    创建一个任务,该任务将在任何提供的任务完成时完成。
    当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“故障”状态结束,结果值也为真。

    static Task> WhenAny(IEnumerable>)
     创建一个任务,该任务将在任何提供的任务完成时完成。
    当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“故障”状态结束,结果值也为真。

    static Task> WhenAny(Task, Task)
    创建一个任务,该任务将在两个任务中的任何一个任务完成时完成。
    当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“已故障”状态结束,也同样如此。


    static Task> WhenAny(Task, Task)案例

     
     var tasklist=new Liststring>>();
    
    Task<string> task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; });
    Task<string> task2 = Task.Run(() => { Console.WriteLine("任务2开始 准备返回的任务ID:"+ Task.CurrentId); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); return "task2complet CurrentId"; });
    Task<string> task3 = Task.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(4000).Wait(); Console.WriteLine("任务3完成"); return "task3complet"; });
    
    tasklist.Add(task1);
    tasklist.Add(task2);
    tasklist.Add(task3);
     
    Taskstring>> taskswait = Task.WhenAny<string>(tasklist.ToArray());
    try
    {
        //等待期间可能发送错误、取消等 所有要捕获异常
        taskswait.Wait();
    }
    catch (OperationCanceledException ex)
    {
        Console.WriteLine(ex.Message);
    }
    
    if (taskswait.Status == TaskStatus.RanToCompletion)
    {
    
        Console.WriteLine("返回的任务ID:"+ taskswait.Result.Id);  
    }
    else
    {
    
    
    }
    Console.Read();
    C