前导
IAsyncResult BeginGetResponse(AsyncCallback callback, object state) WebResponse EndGetResponse(IAsyncResult asyncResult)
Event-based Asynchronous Pattern(EAP)模型以MethodAsync(...) 和CancelAsync(...) 结对出现,由Completed事件设置回调函数。
.Net4.5开始Task Parallel Library(TPL) 为异步和并行编程提供新的模型,使异步和并发操作有统一的编程入口,
该模型常定义以Async后缀结尾的函数名、返回带有awaitable属性的Task/Task<T>对象, 如果你的program target设置为4.5+,可用Task-based Asynchronous Pattern (TAP)取代以上2种模型。
TAP
TPL的核心是Task类,Task,Task<TResult>可以将其理解为一个包装委托(通常就是Action或Func委托)并执行的容器,有了Task几乎不用去和Thread 打交道,使用者只需要关注具体业务对应的Job,Task背后有一个 TaskScheduler 的类来负责调度Task的执行,这样Task对象将在默认的TaskScheduler调度下执行,TaskScheduler使用线程池中的线程,至于是新建还是使用已有线程这个对用户是完全透明的,也可以通过重载函数的参数传入自定义的TaskScheduler。
Task任务状态:
明确Task和线程的关系:
- 任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行
- 任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,在.NET面向任务异步编程模型中,你只需要关注业务概念的任务,具备底层实现会由Task包装完成。
- ThreadPool不支持线程取消、完成、失败通知等交互新操作
- ThreadPool不支持线程执行的先后顺序。
await/async 语法糖
TAP异步编程模型的核心是塑造异步操作的Task、Task<T>对象,这是awaitable 对象,await/async语法糖简化了写法
对于I/O-bound 代码,编写一个返回Task或Task<T>的async方法, 之后await 这个方法
对于CPU-bound代码,使用Task.Run方法后台启动一个操作,之后await这个操作。
魔法发生在await关键字,会将控制权上交给执行Async方法的上层调用者。
在C#语言底层,编译器将你的await/async 代码转换为状态机, 记录了当await发生时控制权上交和后台工作完成时恢复执行的标记。
异步代码可用于I/O -bound 和CPU-bound 代码, 但是2个场景的写法是不同的
异步编程利用Task和Task<T>对象来 塑造需要在后台完成的工作
async关键字将方法转变为异步方法,这样可在方法体使用await关键词, 如果async方法内不包含await关键词,那将不会上交控制权
当await动作发生时,将会暂停(注意是suspend 而不是block)方法,并将控制权上交给调用者(直到awaitable任务完成)
await 只能被用在async方法内部
执行操作的“异步方式”
下面是一个I/O-bound的例子:
using System; using System.Threading; using System.Threading.Tasks; using System.Net.Http; namespace Test { class Program { static void Main(string[] args) { var asyncMethod = AccessTheWebAsync(); Console.WriteLine("go on ...... "+ Thread.CurrentThread.ManagedThreadId ); // 等待异步线程处理完毕,没有以下句子,await使控制回到调用方,主线程即终止。 asyncMethod.Wait(); } public static async Task<int> AccessTheWebAsync() { HttpClient client = new HttpClient(); // GetStringAsync returns a Task<string>. // That means that when you await the task you'll get a string (urlContents). Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); // You can do work here that doesn't rely on the string from GetStringAsync. DoIndependentWork(); // The await operator suspends AccessTheWebAsync. // - AccessTheWebAsync can't continue until getStringTask is complete. // - Meanwhile, control returns to the caller of AccessTheWebAsync. // - Control resumes here when getStringTask is complete. // - The await operator then retrieves the string result from getStringTask. string urlContents = await getStringTask; Console.WriteLine(urlContents.Length+"....... "+Thread.CurrentThread.ManagedThreadId ); // The return statement specifies an integer result. // Any methods that are awaiting AccessTheWebAsync retrieve the length value. return urlContents.Length; } public static void DoIndependentWork() { Console.WriteLine("work ......"+Thread.CurrentThread.ManagedThreadId); } } }
以上代码在ASP.NET 或GUI程序可能会发生死锁, 具体参见《.NET异步编程系列3:掌握SynchronizationContext避免deadlock》;控制台程序经过验证在.NET Core 和.Net Framework上都没有SynchronizationContext,故不会发生死锁。
Task对象提供了丰富的API帮助我们完成 基于任务的异步操作, 让我们专注业务概念的任务。
感谢您的认真阅读,如有问题请大胆斧正,如果您觉得本文对你有用,不妨右下角点个或加关注。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置注明本文的作者及原文链接,否则保留追究法律责任的权利。