提起.Net中的 async/await,相信不少.neter 第一反应都会是异步编程,其本质是语法糖,但继续追查下去,既然是语法糖,那么通过编译以后,真正的代码是什么样的,如何执行的?带着这些疑问,经过网上资料的查询,能够了解到编译以后,是经过实现 IAsyncStateMachine 的一个状态机来实现的,博客园里大神Jeffcky 已经说得很清楚了,传送门: http://www.javashuo.com/article/p-tvpqjkma-gq.htmlhtml
上述知识对咱们理解 async/await 很是重要,但不是本文讨论的侧重点,触发笔者写这篇文章的初衷是:git
答案固然不是,google了一圈后发现,当一个类能够被await,必须知足如下条件:github
a. 它必须包含 GetAwaiter() 方法(实例方法或者扩展方法) // 手动划重点:扩展方法,聪明的你是否是立马有些思想火花编程
b. GetAwaiter() 返回awatier实例,而且这个实例包含以下条件:异步
- 必须实现 INotifyCompletion 或者 ICriticalNotifyCompletion 接口
- 必须包含 IsCompleted 公共属性
- 必须包含 GetResult() 方法,返回void或者其余返回值
上述条件中INotifyCompletion 接口信息以下:async
// // 摘要: // Represents an operation that schedules continuations when it completes. public interface INotifyCompletion { // // 摘要: // Schedules the continuation action that's invoked when the instance completes. // // 参数: // continuation: // The action to invoke when the operation completes. // // 异常: // T:System.ArgumentNullException: // The continuation argument is null (Nothing in Visual Basic). void OnCompleted(Action continuation); }
重点上述对于参数 continuation 的解释:委托在操做完成以后调用。此处遗留一个问题:在谁的操做完成以后调用,是怎么调用的?异步编程
先把上述问题放一边,咱们来本身写一个能够被await的类,而且观察先后执行的顺序以及是否存在线程切换:函数
public class Program { static async Task Main (string[] args) { Console.WriteLine ($"Begin awati,thread id is {Thread.CurrentThread.ManagedThreadId}"); int result = await new CustomAwaitable (); Console.WriteLine ($"End await,result is {result},thread id is {Thread.CurrentThread.ManagedThreadId}"); await Task.Delay (Timeout.Infinite); } } public class CustomAwaitable : INotifyCompletion { public void OnCompleted (Action continuation) { Console.WriteLine ($"Invoke continuation action on completed,thread id is {Thread.CurrentThread.ManagedThreadId}"); continuation?.Invoke (); } public int GetResult () { Console.WriteLine ($"Get result,thread id is {Thread.CurrentThread.ManagedThreadId}"); return 100; } public bool IsCompleted { get; set; } public CustomAwaitable GetAwaiter(){ return this; } }
上述代码中,CustomAwaitable 实例知足了可被await的全部条件,而且正常经过编译,运行后发现结果以下:源码分析
PS D:\git\awaitable\src> dotnet run Begin main,thread id is 1 Get awatier,thread id is 1 Begin Invoke continuation action on completed,thread id is 1 Get result,thread id is 1 End main,result is 100,thread id is 1 End Invoke
根据上述日志,能够看出:学习
- 执行先后线程并未发生切换,因此当咱们不假思索的回答 await/async 就是异步编程时,至少是一个不太严谨的答案
- 最后执行日志 "End Invoke" 代表:continuation action 这个委托,根据上述调用日志顺序能够大体理解为:编译器将await以后的代码封装为这个 action,在实例完成后调用OnCompleted方法执行了await 以后的代码(注:实际状况比较复杂,若是有多行await,会转换为一个状态机,具体参看文章开头给出的链接)。
上述红框代码显示,Task在GetAwaiter中建立了 TaskAwaiter对象,并将this传递。
看到此处,有了前面的知识,咱们会对await task有了更加深刻的理解:
Task经过增长一个GetAwatier()函数,同时将自身传递给TaskAwaiter类来实现了await语法糖的支持,同时在执行时,调用GetResult()函数的本质是经过 Task.Wait等待异步线程的执行完成,而后经过回调进行后续的操做。
本文主要对 async/await 语法糖进行分析验证,同时经过对Task源码分析,更加深刻的理解此语法糖自己的语法,相信经过经过此文,对你们从多个角度去理解异步编程有帮助,我本身也在不停的学习。
本文代码示例地址:https://github.com/xBoo/awaitable