C# Task 循环任务_C# 异步编程

(给DotNet加星标,提升.Net技能)

转自: yswenli cnblogs.com/yswenli/p/11987377.html

前言

基于Task的异步编程模式(TAP)是Microsoft为.Net平台下使用Task进行编程所提供的一组建议,这种模式提供了可以被await消耗(调用)方法的APIs,并且当使用async关键字编写遵守这种模式的方法时,手写Task通常很有用。通常TAP用起来与普通方式没什么两样,但是不支持ref和out参数。

任务和线程的区别:

1、任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。

2、任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。

3、Task的优势

ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:

  • ThreadPool不支持线程的取消、完成、失败通知等交互性操作;

  • ThreadPool不支持线程执行的先后次序;

以往,如果开发者要实现上述功能,需要完成很多额外的工作,现在,微软提供了一个功能更强大的概念:Task。Task在线程池的基础上进行了优化,并提供了更多的API。

下面分析一理异步编程中的一些关键点

一、Await  

我们都知道Await关键字是.NET FrameWork4.5引入的特性。

Await使得我们使用异步更加时特别便捷,并且还不会导致线程堵塞。

我们在使用时也就莫名其妙的使用。

往往不知道为什么不会导致线程堵塞。在这里,简单的谈论下Await的一点原理。

在C#并行编程这本书中是这么介绍await的:async方法在开始时以同步方式执行,在async方法内部,await关键字对它参数执行一个异步等待,它首先检查操作是否已经完成,如果完成,就继续运行(同步方式),否则,会暂停async方法,并返回.留下一个未完成的task一段时间后,操作完成,async方法就恢复执行.

看到这句话应该就差不多能想到await为什么不会导致线程堵塞了,当碰到await时如果没有执行成功就先暂停这个方法的执行,执行方法外以下代码,等await操作完成后再执行这个方法await之后的代码。

private  void button1_Click(object sender, EventArgs e){

运行后会发现在点击button按钮时窗体不能被移动了,然后等待了3秒钟才弹出"同步代码"这句话。

async Task DemoAsync(){

await Task.Run(() => { Thread.Sleep(3000); });
Thread.Sleep(3000);
}

再次运行就会神奇的发现,此时会先弹出"同步代码"这句话,然后等待3秒后窗体就不能被移动。

二、Task

Task 类表示通常以异步方式执行的单个操作, Task 对象是基于任务的异步模式的中心组件之一。

由于 Task 对象执行的工作通常在线程池线程上异步执行,而不是在主应用程序线程上同步执行,因此可以使用 Status 属性,还可以使用 IsCanceled、IsCompleted和 IsFaulted 属性,用于确定任务的状态。通常,lambda 表达式用于指定任务要执行的工作。可以通过多种方式创建 Task 实例。

最常见的方法(从 .NET Framework 4.5开始提供)是调用静态 Run 方法。Run 方法提供一种简单的方法来使用默认值启动任务,而无需其他参数。Task 类还提供了初始化任务的构造函数,但不计划执行该任务。

出于性能原因,Task.Run 或 TaskFactory.StartNew 方法是用于创建和计划计算任务的首选机制,但对于必须分隔创建和计划的情况,可以使用构造函数,然后调用 Task.Start用于计划任务稍后执行的方法。因为任务通常在线程池线程上以异步方式运行,所以,创建和启动任务的线程会在实例化任务后立即继续执行。

在某些情况下,当调用线程是主应用程序线程时,应用程序可能会在任何任务实际开始执行之前终止。在其他情况下,应用程序的逻辑可能要求调用线程在一个或多个任务完成执行时继续执行。

可以通过调用 Wait 方法来等待一个或多个任务完成,从而同步调用线程的执行以及它启动的异步任务。若要等待单个任务完成,可以调用其 Task.Wait 方法。

Wait(Int32) 和 Wait(TimeSpan) 方法会阻止调用线程,直到任务完成或超时间隔结束。

需要注意的是,Task中的方法一般是不会将异常抛出的,哪怕是Task这种,这需要开发人员自行处理。不过如果获取Task.Result则会将任务内的异常抛出来;

public static async Task Hello(int a=0){

Task.Factory提供了更多参数的Task创建方式以支持自定义的Task,比如:Task,Factory.StartNew(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)就支持指定取消通知参数、任务创建模式,指定任务调度器;其中TaskCreationOptions参数意义如下:

C# Task 循环任务_C# 异步编程_第1张图片

三、Task.ConfigureAwait

在Task里中有ConfigureAwait这么一个方法,这个方法是干什么的呢,我们先看下方法注释是怎么解释这个方法的:"尝试将延续任务封送回原始上下文,则为 true;否则为 false"。光看这段代码并看不出什么,然后我们再看这么一段话:"一个async方法是由多个同步执行的程序块组成.每个同步程序块之间由await语句分隔.用await语句等待一个任务完成.当该方法在await处暂停时,就可以捕捉上下文(context).如果当前SynchronizationContext不为空,这个上下文就是当前SynchronizationContext.如果为空,则这个上下文为当前TaskScheduler.该方法会在这个上下文中继续运行.一般来说,运行UI线程时采用UI上下文,处理ASP.NET请求时采用ASP.NET请求上下文,其它很多情况则采用线程池上下文"。

这句话已经基本讲明了其实后续代码会下上文中执行。这个上下文一般是UI上下文(运行在UI上)或请求上下文(ASP.NET) 这两个可以说是原始上下文,而其它情况采用线程池上下文,也就是开辟一个新线程。这么说也就是ConfigureAwait方法是将后续代码是送到原始上下文还是线程池上下文中。

下面稍微修改下刚才的方法:

async Task DemoAsync()

ConfigureAwait(false)将后续代码交给线程池来执行,也就是上面的Thread.Sleep并不会阻塞窗体。

四、Task.Delay

在异步编程中,一般不建议使用Thread.Sleep,而是使用粒度更小的Task.Delay;Thread.Sleep、Thread.Yeild等会让当前工作线程阻塞,而Task.Delay可以让当前线程空出来去完成其他的Task。

new CancellationTokenSource();

五、SemaphoreSlim

表示对可同时访问资源或资源池的线程数加以限制的 Semaphore 的轻量替代, SemaphoreSlim类是用于在单个应用内进行同步的建议信号量。

在异步编程过程中,建议使用SemaphoreSlim.Wait()、SemaphoreSlim.Release()来替换Lock,因为lock只能由当前线程来解锁,而SemaphoreSlim可以由任意线程来解锁。

六、CancellationTokenSource 

启动一个Task去做一些事,如果希望它在某个阶段去被动的停止,可以使用这个CancellationTokenSource对象,把它注入到Task里,使用当外界触发Cancel()方法或设置了超时时,这个Task就会被取消。

通常和CancellationToken.IsCancellationRequested一起配合Task来使用。

public static async void Loop(int timeOut = 5 * 1000){

也可以CancellationTokenSource.CreateLinkedTokenSource来关联多个CancellationTokenSource

using (CancellationTokenSource cts = 

七、TaskCompletionSource

表示未绑定到委托的 Task 的制造者方,并通过 Task 属性提供对使用者方的访问。

从这个官方解释上看不出这个到底有什么作用,其实这是一种受用者控制创建Task的方式。你可以使Task在任何你想要的时候完成,你也可以在任何地方给它一个异常让它失败。

这个可以实现事件通知类似的功能;具体就是说TaskCompletionSource如果不进行SetResult或SetException的时候,TaskCompletionSource所委托的的Task是不会有Result,这个Task会一直等待TaskCompletionSource来赋值;这样就极大的简化了异步事件或异步通知的实现。

public static async Task SetValue(int a = 1){

推荐阅读

(点击标题可跳转阅读)

.NET Core 3.0新增的异步流 (Async)

重新认识 Async/Await 语法糖

C#并发编程之异步编程(线程讨论)

看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

C# Task 循环任务_C# 异步编程_第2张图片

好文章,我在看❤️

你可能感兴趣的:(C#,Task,循环任务)