【C# Task】开篇
阅读原文时间:2023年07月10日阅读:2

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

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

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

2、2座任务工厂 TaskFactory/TaskFactory

3、2种TaskCompletionSource/TaskCompletionSource 任务完成源

4、3种类型的TaskScheduler任务调度器

5、7种任务选项 TaskCreationOption

6、8个TaskStatu 任务状态

7、15种后续任务选项TaskContinuationOptions

8、【C# Task】System.Threading.Channels 生产者和消费者模式

9、【C# Task】 ValueTask/Task

10、取消任务 CanellationTokenSource

11、任务并行

12、异步任务 async/await

任务并行(task parallelism)是 PFX 中最底层的并行方式。这一层次的类定义在System.Threading.Tasks命名空间中,如下所示:

作用

Task

管理工作单元

Task<TResult>

管理有返回值的工作单元

TaskFactory

创建任务

TaskFactory<TResult>

创建有相同返回类型的任务和任务延续

TaskScheduler

管理任务调度

TaskCompletionSource

手动控制任务的工作流

valueTask

手动控制任务的工作流

valueTask[`<TResult>`](https://blog.gkarch.com/threading/part5.html#creating-and-starting-tasks)

手动控制任务的工作流

本质上,任务是用来管理可并行工作单元的轻量级对象。任务使用 CLR 的线程池来避免启动独立线程的开销:它和ThreadPool.QueueUserWorkItem使用的是同一个线程池,在 CLR 4.0 中这个线程池被调节过,让Task工作的更有效率(一般来说)。

本质上,任务是用来管理可并行工作单元的轻量级对象。任务使用 CLR 的线程池来避免启动独立线程的开销:它和ThreadPool.QueueUserWorkItem使用的是同一个线程池,在 CLR 4.0 中这个线程池被调节过,让Task工作的更有效率(一般来说)。

需要并行执行代码时都可以使用任务。然而,它们是为了充分利用多核而调节的:事实上,Parallel类和PLINQ内部就是基于任务并行构建的。

任务并不只是提供了简单高效的使用线程池的方式。它们还提供了一些强大的功能来管理工作单元,包括:

任务也实现了局部工作队列(local work queues),这个优化能够让你高效的创建很多快速执行的子任务,而不会带来单一工作队列会导致的竞争开销。

使用场合:TPL 可以让你使用极小的开销创建几百个(甚至几千个)任务,但如果你要创建上百万个任务,那需要把这些任务分成大一些的工作单元才能有效率。Parallel类和 PLINQ 可以自动实现这种工作分解。

任务的初始化配置 :

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() 采用的是 默认的工厂设置。

    1、var task = Task.Run(() => Console.WriteLine("cancelled"));
    2、 var task2 = Task.Factory.StartNew(() =>
    {
    //TODO you code
    });
    3、var t = Task.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.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();

    AsyncState指定状态对象

    当创建任务实例或调用Task.Factory.StartNew时,可以指定一个状态对象(state object),它会被传递给目标方法。如果你希望直接调用方法而不是 lambda 表达式,则可以使用它。

    static void Main()
    {
    var task = Task.Factory.StartNew (Greet, "Hello");
    task.Wait(); // 等待任务结束
    }

    static void Greet (object state) { Console.Write (state); } // 打印 "Hello"

    因为 C# 中有 lambda 表达式,我们可以更好的使用状态对象,用它来给任务赋予一个有意义的名字。然后就可以使用AsyncState属性来查询这个名字:

    static void Main()
    {
    var task = Task.Factory.StartNew (state => Greet ("Hello"), "Greeting");
    Console.WriteLine (task.AsyncState); // 打印 "Greeting"
    task.Wait();
    }

    static void Greet (string message) { Console.Write (message); }

    Visual Studio 会在并行任务窗口显示每个任务的AsyncState属性,所以指定有意义的名字可以很大程度的简化调试。

    Task.Delay延迟任务执行

    内部用有一个TimerQueueTimer定时器,该定时器内部有一个TimerQueue类型的定时器数组,数组大小等于cpu数。等到时间到了,就通知线程池执行定时委托任务,然后将该定时器从队列中删除。

    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 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 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 List>();

    Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; });
    Task task2 = Task.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); return "task2complet"; });
    Task task3 = Task.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); return "task3complet"; });

    tasklist.Add(task1);
    tasklist.Add(task2);
    tasklist.Add(task3);

    Tasktaskswait = Task.WhenAll(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 List>();

    Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; });
    Task task2 = Task.Run(() => { Console.WriteLine("任务2开始 准备返回的任务ID:"+ Task.CurrentId); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); return "task2complet CurrentId"; });
    Task task3 = Task.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(4000).Wait(); Console.WriteLine("任务3完成"); return "task3complet"; });

    tasklist.Add(task1);
    tasklist.Add(task2);
    tasklist.Add(task3);

    Task> taskswait = Task.WhenAny(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();

    Task.Yield

    await Task.Yield和Thread.yield 一个意思,让出一下当前task线程,让有需要的任务先运行,当前线程没有其他任务 那么他就继续执行。 会捕获同步上下文,如果同步上下文为null。就使用当前TaskScheduler。

    源代码:

    SynchronizationContext? syncCtx = SynchronizationContext.Current;
    if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
    {
    syncCtx.Post(s_sendOrPostCallbackRunAction, continuation);
    }
    else
    {
    // If we're targeting the default scheduler, queue to the thread pool, so that we go into the global
    // queue. As we're going into the global queue, we might as well use QUWI, which for the global queue is
    // just a tad faster than task, due to a smaller object getting allocated and less work on the execution path.
    TaskScheduler scheduler = TaskScheduler.Current;
    if (scheduler == TaskScheduler.Default)

    private async void button_Click(object sender, EventArgs e)
    {
    await Task.Yield(); // Make us async right away

      var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later
    
      await UseDataAsync(data);  

    }

    Task.ConfigureAwait(false)

    表示await/async`异步中`保证回调不会被排队回到原始上下文中。ConfigureAwait(false)在框架库中使用,通用库是“通用的”,部分原因是它们不关心使用它们的环境。在ui环境下``await/async`异步中` ``避免使用ConfigureAwait(false)。异步使用会导致后续代码无法回到主线程,导致bug,如下错误用法:

    private static readonly HttpClient s_httpClient = new HttpClient();

    private async void downloadBtn_Click(object sender, RoutedEventArgs e)
    {
    string text = await s_httpClient.GetStringAsync("http://example.com/currenttime").ConfigureAwait(false); // bug
    downloadBtn.Content = text;//将在默认同步上下文中执行 bug
    }

    Task.FromResult(TResult)

    创建一个返回值为TResult类值的Task实列。

    Task GetCustomerIdAsync()
    {
    return Task.FromResult(1123);
    }
    Console.WriteLine(GetCustomerIdAsync().GetAwaiter().GetResult());
    //输出:1123

    Task.FromException 方法

    在通过task.Result和task.GetAwaiter().GetResult()获取该类实列的结果时,将触发异常。

    Task GetCustomerIdAsync()
    {
    return Task.FromException(new OperationCanceledException("dfdfsdf"));
    }

    try
    {//将触发异常
    string sdf= GetCustomerIdAsync().Result;
    }
    // Ignore exceptions here.
    catch (AggregateException) { }
    Console.WriteLine(GetCustomerIdAsync().GetAwaiter().GetResult() );//将触发异常

    Task.FromCanceled 方法

    返回一个带有取消标记的task实列。尚未对 cancellationToken 请求取消;其 IsCancellationRequested 属性为 false

    CancellationTokenSource cts=new CancellationTokenSource();
    Task GetCustomerIdAsync()
    {
    return Task.FromCanceled(cts.Token);
    }
    //要执行,
    cts.Cancel();

    Console.WriteLine(GetCustomerIdAsync().Status);//如果不执行cts.Cancel();将触发ArgumentOutOfRangeException异常

    取消任务

    可在创建Task时将一个CancellationToken传给构造器,从而将两者相关联,如果CancellationToken在Task调度前取消,那么Task就会被取消,永远都不执行。但如果Task已调度,那么Task的代码就只支持显示取消,其操作才能在执行期间取消,遗憾的是,虽然Task关联了一个CancellationToken,但却没有办法访问他。因此,必须在Task的代码中获得创建Task对象时的同一个CancellationToken。为此,最简单的办法就是使用一个Lamda表达式,将CancellationToken作为闭包变量传递。

    使用同一个CancellationTokenSource取消多个任务

    只需要多个task使用相同的CancellationTokenSource.Token即可,将上面的代码稍微改动下:

    public static void Main(string[] args)
    {
    CancellationTokenSource cts = new CancellationTokenSource();
    for (int i = 0; i < 5; i++) { var msg = "xiaoming" + (i + 1); var task = new Task(() => Run(msg, cts.Token), cts.Token);
    task.Start();
    }
    Thread.Sleep(5000);
    cts.Cancel();
    Console.WriteLine("ok!");
    Console.ReadLine();
    }

    多个 CancellationTokenSource 复合使用

    这种应用的场景为:当有多个CancellationTokenSource用来作用于一个异步任务的时候,你想达到其中一个CancellationTokenSource取消就取消这个异步任务的效果。
    看下面代码:

    using System;
    using System.Collections;
    using System.Data;
    using System.IO;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;

    namespace TestDI
    {
    class Program
    {
    public static void Main(string[] args)
    {
    CancellationTokenSource cts1 = new CancellationTokenSource();
    CancellationTokenSource cts2 = new CancellationTokenSource();
    CancellationTokenSource cts3 = new CancellationTokenSource();
    CancellationTokenSource compositeCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token, cts3.Token);
    var task = new Task(() => Run("xiaoming", compositeCts.Token), compositeCts.Token);
    task.Start();
    Thread.Sleep(5000);
    cts1.Cancel();
    Console.WriteLine("ok!");
    Console.ReadLine();
    }

        public static void Run(object state, CancellationToken token)  
        {  
            while (true)  
            {  
                if (token.IsCancellationRequested)  
                {  
                    return;  
                }  
                else  
                {  
                    Thread.Sleep(1000);  
                    Console.WriteLine($"state={state},任务线程:{Thread.CurrentThread.ManagedThreadId},是否是守护线程:{Thread.CurrentThread.IsBackground},是否是线程池:{Thread.CurrentThread.IsThreadPoolThread}");  
                }  
            }  
        }  
    }  

    }

    ConfigureAwait(false)是否保证回调不会在原始上下文中运行?

    不。它保证它不会被排队回到原始上下文中……但这并不意味着await task.ConfigureAwait(false)之后的代码仍无法在原始上下文中运行。那是因为等待已经完成的等待对象只是保持await同步运行,而不是强迫任何东西排队。因此,如果您await的任务在等待时已经完成,无论您是否使用过ConfigureAwait(false),紧随其后的代码将在当前上下文中继续在当前线程上执行。

    Task在同一个线程运行的三种方式

    //当前线程
    Console.WriteLine("是否是线程池线程:" + Thread.CurrentThread.IsThreadPoolThread+Environment.CurrentManagedThreadId);
    //第一种方式 Continuetask和第taskA在同一个线程运行
    SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
    if (SynchronizationContext.Current != null)
    {
    Task taskA = new(() => Console.WriteLine("是否是线程池线程" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId));
    taskA.ContinueWith((t) =>{ Console.WriteLine("是否是线程池线程:" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId); },TaskContinuationOptions.ExecuteSynchronously);
    taskA.Start();

    }
    else { Console.WriteLine("当前上下文为空"); }

    //第二种方式 task在当前线程运行
    Console.WriteLine("是否是线程池线程:" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId);
    if (SynchronizationContext.Current != null)
    {
    Task task = new(() => Console.WriteLine("是否是线程池线程" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId));
    task.RunSynchronously();

    }
    Console.Read();

    //第三种方式 task在当前线程运行 在ui线程中可行,控制台线程使用的默认同步上下文,不可行
    SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
    if (SynchronizationContext.Current != null)
    {
    Task task2 = new(() => Console.WriteLine("是否是线程池线程" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId));
    task2.Start(TaskScheduler.FromCurrentSynchronizationContext());

    }
    else { Console.WriteLine("当前上下文为空"); }

    手机扫一扫

    移动阅读更方便

    阿里云服务器
    腾讯云服务器
    七牛云服务器