重新认识 async/await 语法糖
提起.Net中的 async/await,相信很多.neter 第一反应都会是异步编程,其本质是语法糖,但继续追查下去,既然是语法糖,那么经过编译之后,真正的代码是什么样的,如何执行的?带着这些疑问,通过网上资料的查询,可以了解到编译之后,是通过实现 IAsyncStateMachine 的一个状态机来实现的,博客园里大神Jeffcky 已经说得很清楚了,传送门: https://www.cnblogs.com/CreateMyself/p/5983208.html
上述知识对我们理解 async/await 非常重要,但不是本文讨论的侧重点,触发笔者写这篇文章的初衷是:
1.只有Task可以被await吗,await之后就一定是异步执行吗?
答案当然不是,google了一圈后发现,当一个类可以被await,必须满足以下条件:
a.它必须包含 GetAwaiter() 方法(实例方法或者扩展方法) // 手动划重点:扩展方法,聪明的你是不是立马有些思想火花
b.GetAwaiter() 返回awatier实例,并且这个实例包含如下条件:
- 必须实现 INotifyCompletion 或者 ICriticalNotifyCompletion 接口
- 必须包含 IsCompleted 公共属性
- 必须包含 GetResult() 方法,返回void或者其他返回值
上述条件中INotifyCompletion 接口信息如下:
//
// 摘要:
// 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,会转换为一个状态机,具体参看文章开头给出的连接)。
2.了解了上述知识之后,那么我们常规所说的await Task异步编程又是怎么回事呢?
- 先来看Task部分源码(传送门):

上述红框代码显示,Task在GetAwaiter中创建了 TaskAwaiter对象,并将this传递。
- 再来看TaskAwaiter源码(传送门):


看到此处,有了前面的知识,我们会对await task有了更加深入的理解:
Task通过增加一个GetAwatier()函数,同时将自身传递给TaskAwaiter类来实现了await语法糖的支持,同时在执行时,调用GetResult()函数的本质是通过 Task.Wait等待异步线程的执行完成,然后通过回调进行后续的操作。
总结
本文主要对 async/await 语法糖进行分析验证,同时通过对Task源码分析,更加深入的理解此语法糖本身的语法,相信通过通过此文,对大家从多个角度去理解异步编程有帮助,我自己也在不停的学习。
本文代码示例地址:https://github.com/xBoo/awaitable
重新认识 async/await 语法糖的更多相关文章
- 抓住异步编程async/await语法糖的牛鼻子: SynchronizationContext
长话短说,本文带大家抓住异步编程async/await语法糖的牛鼻子: SynchronizationContext 引言 C#异步编程语法糖async/await,使开发者很容易就能编写异步代码. ...
- Async/await语法糖实现(Generator)
// generator也是一种迭代器(Iterator) 有next方法,并返回一个对象{value:...,done:...} function run(generatorFunction) { ...
- javascript异步编程之generator(生成器函数)与asnyc/await语法糖
Generator 异步方案 相比于传统回调函数的方式处理异步调用,Promise最大的优势就是可以链式调用解决回调嵌套的问题.但是这样写依然会有大量的回调函数,虽然他们之间没有嵌套,但是还是没有达到 ...
- Python PEP 492 中文翻译——协程与async/await语法
原文标题:PEP 0492 -- Coroutines with async and await syntax 原文链接:https://www.python.org/dev/peps/pep-049 ...
- ReactNative踩坑日志——使用async/await语法解决网络请求的异步导致的指令执行顺序错乱问题
转载请注明原文地址: ReactNative的fetch是天然的异步请求,因此,如果你在一个代码块中使用了fetch,那么在执行的时候程序不会等待网络响应结束才执行下一条代码,而是会直接按顺序执行完整 ...
- C#Framework4.0支持异步async/await语法
由于用户使用的是XP系统,但是程序里异步都是通过async/await代码来实现的,然而async/await需要Framework4.5版本才可以,而XP系统最高只能支持到Framework4.0, ...
- ES8 async/await语法
Async/await的主要益处是可以避免回调地狱(callback hell)问题 Chromium JavaScript引擎 从v5.5开始支持async/await功能,Chromium Jav ...
- python3.6以上 asyncio模块的异步编程模型 async await语法
这是python3.6以上版本的用法,本例是python3.7.2编写使用asyncio模块的异步编程模型,生产这消费者,异步生产,用sleep来代替IO等待使用async和await语法来进行描述a ...
- 让webpack打包支持ES7的async/await语法
npm install --save-dev babel-plugin-transform-runtime npm install --save babel-runtime .babelrc配置 { ...
随机推荐
- Have You Tried Delphi on Amazon Linux? (就是AWS用的Linux)
The new Delphi Linux compiler enables customers to take new or existing Windows server applications ...
- DELPHI编写服务程序总结(在系统服务和桌面程序之间共享内存,在服务中使用COM组件)
DELPHI编写服务程序总结 一.服务程序和桌面程序的区别 Windows 2000/XP/2003等支持一种叫做“系统服务程序”的进程,系统服务和桌面程序的区别是:系统服务不用登陆系统即可运行:系统 ...
- Mac App Store应用签名和pkg签名(必须签名后才能销售)
App签名 只有用苹果颁发的证书签名的应用才能在App Store上进行销售,所以我们开发的应用必须打上签名. 签名有两种方式,一是使用Xcode,在配置里面设置签名,编译出来的app就有了签名:二是 ...
- AndroidStudio下的依赖管理
在开发中用第三方库是很常见的事,如何在AndroidStudio下管理这些依赖呢?这就是这篇文章的目的. 目录 Maven/Ivy仓库依赖 Module依赖 aar文件依赖 jar文件依赖 例子完整代 ...
- 基于X.509证书和SSL协议的身份认证过程实现(OpenSSL可以自己产生证书,有TCP通过SSL进行实际安全通讯的实际编程代码)good
上周帮一个童鞋做一个数字认证的实验,要求是编程实现一个基于X.509证书认证的过程,唉!可怜我那点薄弱的计算机网络安全的知识啊!只得恶补一下了. 首先来看看什么是X.509.所谓X.509其实是一种非 ...
- .NET错误提示之:无法更新EntitySet“TableName”因为它有一个DefiningQuery
使用LINQ 进行提交数据时发生的错误提示 原因:提交的对象表 没有设主键.
- 解决xp越来越慢的办法(其中有些自动备份的功能)
1.减少磁盘空间占用2.终止不常用的系统服务3.安全问题4.另外一些技巧 首先问一下,你是不是很想激活XP,不...准确的说你是不是想在ms的站上能够升级.如果答案是肯定的话,那我们就先来探讨一下安装 ...
- 动态链接库(Dynamic Link Library)学习笔记(附PE文件分析)
转载:http://www.cnblogs.com/yxin1322/archive/2008/03/08/donamiclinklibrary.html 作者:EricYou 转载请注明出处 注 ...
- “多团队大规模”开发模式 - 基于SAP HANA平台的多团队产品研发
应用SAP HANA “官方”开发模式的伙伴们在转到“多团队大规模”开发模式时会遇到各式各样的心理不适应的状况,各种纠结.比如GIT Repository和HANA Repository冲突什么的. ...
- pycharm窗口选项卡管理
1.主题 我们已经注意到Pycharm的主编辑框是基于窗口选项卡机制显示的,Pycharm选项卡多种多样,这里我们将详细介绍这种选项卡机制. 2.激活的选项卡 每当我们打开一个Python文件时op ...