【C# Task】开篇

概览

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

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

 

 

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

2、2座任务工厂 TaskFactory/TaskFactory<TResult>

3、2种TaskCompletionSource/TaskCompletionSource<TResult> 任务完成源

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

5、7种任务选项 TaskCreationOption

6、8个TaskStatu 任务状态

7、15种后续任务选项TaskContinuationOptions

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

9、【C# Task】 ValueTask/Task<TResult>

10、取消任务 CanellationTokenSource

11、任务并行

12、异步任务 async/await

 

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

作用
Task 管理工作单元
Task<TResult> 管理有返回值的工作单元
TaskFactory 创建任务
TaskFactory<TResult> 创建有相同返回类型的任务和任务延续
TaskScheduler 管理任务调度
TaskCompletionSource 手动控制任务的工作流
valueTask 手动控制任务的工作流
valueTask<TResult> 手动控制任务的工作流

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

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

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

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

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

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

创建Task

任务的初始化配置 :

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

  1. Task(Action) 
  2. Task(Action, CancellationToken)
  3. Task(Action, TaskCreationOptions)
  4. Task(Action<Object>, Object) 
  5. Task(Action, CancellationToken, TaskCreationOptions)  
  6. Task(Action<Object>, Object, CancellationToken) 
  7. Task(Action<Object>, Object, TaskCreationOptions) 
  8. Task(Action<Object>, 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<TResult>()和普通工厂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<TResult> 类构造函数进行实例化。 要同步运行的任务必须处于 Created 状态。 任务只能启动并运行一次。 如果尝试再次计划任务,将导致异常。Task()创建的任务处于 Created状态,必须调用Start运行任务后任务状态才能变成WaitingtoRun。

   Start ()/Start(TaskScheduler):实例方法 运行任务,可以传入任务调度器,或者采用默认线程池任务调度器。通过调用Start()方法执行的任务必须是 Task 或 Task<TResult> 类构造函数进行实例化。 要同步运行的任务必须处于 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<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>)  

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


static WhenAll(paramsTask[])     

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


static Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>>)     

创建一个任务,该任务将在可枚举集合中的所有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<TResult[]>  WhenAll<TResult>(paramsTask<TResult>[])     

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

以上案例如下:

static WhenAll(IEnumerable<Task>)案例

 
 var tasklist=new List<Task>();

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<Task>方法不会阻塞调用线程。
如果提供的任何任务在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<TResult>(IEnumerable<Task<TResult>>)案例

 
 var tasklist=new List<Task<string>>();

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<TResult>>,Task<TResult>是第一返任务。都是静态方法

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


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

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

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

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

 

 


static Task<Task<TResult>> WhenAny<TResult>(Task<TResult>, Task<TResult>)案例

 
 var tasklist=new List<Task<string>>();

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);
 
Task<Task<string>> 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();

 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)

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

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

 Task.FromException 方法

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

Task<string> GetCustomerIdAsync()
{
    return  Task.FromException<string>(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<string> GetCustomerIdAsync()
{
    return  Task.FromCanceled<string>(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("当前上下文为空"); }

 

posted @ 2022-02-09 16:32  小林野夫  阅读(948)  评论(0编辑  收藏  举报
原文链接:https://www.cnblogs.com/cdaniu/