这一个系列的文章主要来讲 C# 中的语言特性 async-await 在语言层面的本质,我们都知道 await 是编译器进行了一个 rewrite,然而这个 rewrite 并不是直接 rewrite 成其他没有原生支持 await 的语言的 lambda 回调的形式,而是整个对方法进行了重写,下面就让我们来从最简单的方法,一步一步剖析 await 糖的工作机制。

一个 async 方法,就是你在代码执行到一半的时候,告诉电脑:我要把函数返回,你先去干别的事情(比如 UI 操作),等我这边的事完成之后,再回复现场继续从刚才返回的地方执行。方法的执行是依靠状态机驱动的,一系列的 MoveNext 方法推动状态机的执行,编译器则会将方法分而治之,把 async 方法体拆分成许多部分,每一部分是一个状态机中的状态,放进 MoveNext 中

新建一个控制台工程,我们从最简单的 async 方法开始:FooAsync

编译器已经提示我这个 async 标识符没卵用了,没关系

然后我们再写一个方法来对照被编译器重写过的方法:FooAsync2

这还没完,一个异步方法中执行的各项操作,运算,中间暂停然后返回,最后 return 结果,是由状态机推动的,所以我们手动 rewrite 的 FooAsync2 方法中需要一个状态机,并且要实现系统的 IAsyncStateMachine 接口。

state machine 是一个 struct,默认情况下一个 async 方法不需要等待,所以我们不希望有一个在堆上的东西来增加我们的运行负担。

你可以看到这个接口要求两个方法,第二个我们之后再讲,你可能会奇怪为什么一个 IAsyncStateMachine 实例需要 SetStateMachine 另一个实例(后面会说,这其实是他自己),由于我们的 state machine 是一个 struct,所以当方法 await 的时候,整个栈就回退给其他方法来用了,所以你需要把这个 state machine 以及其他的参数转移到堆上,然后用这个方法来获得他自己,这个时候堆上的对象的运行时消耗才是值得的。而 MoveNext 就是之前我们说过的用来推动整个方法运行的方法。

方法里已经装了一个 state machine 的实例了,然后我们来看看谁要来驱动着方法一步一步向前走,当控制流回到方法内部的时候他需要来执行一些操作,尽管我们这里的方法什么都没干,我们还是要把所有的机构都弄好。我们需要一个 AsyncMethodBuilder,这个东西也已经在 System.Runtime.CompilerServices 里面提供了。

这个 method builder 放在了状态机内,因为方法可能返回而我们一直需要它,所以要借助 state machine 转移到栈上的同时把它一起转移了。AsyncVoidMethodBuilder 也是一个 struct,同样也是我们不希望堆上的东西增加额外的运行时负担。

method builder 是我们在控制流开始,await,返回的时候应该去使用的东西,这个东西应该在 MoveNext 函数中被使用,这里我们的方法什么都没干,所以 MoveNext 中只有一个状态的转移:开始->返回。返回通过 method builder 的 SetResult 方法完成,所有的 return 都会被 rewrite 成 SetResult。

最后方法怎么开始呢?我们看看 method builder 有没有 Start 方法,哈!有。Start 方法需要 state machine 的引用,为什么需要,因为 method builder 需要在方法一步步进行的时候调用 ModeNext,为什么要引用,这也说的通,方法的状态只需要一份,使用引用,避免拷贝,以及 state machine 有可能在栈上(现在这样)也有可能在堆上,需要用引用来指向它。

现在的 FooAsync2 就是 async 方法 FooAsync 被 rewrite 之后的样子。我们在 Main 里调用 FooAsync2,单步执行

从调用堆栈看,Start 方法调用了 MoveNext,然后我们的 MoveNext 方法就像 FooAsync 一样,什么都没干,直接 SetResult 然后返回,然后 Start 返回,FooAsync2 返回,Main 返回,一个没卵用的异步方法完成了。

这样就是一个最简单的异步方法被 rewrite 之后的样子,这一篇就到这里,下一篇讲讲稍微复杂点的方法。

Async Programming - 1 async-await 糖的本质(1)的更多相关文章

  1. Async Programming - 1 async-await 糖的本质(2)

    上一篇讲了这么多,其实说的就是一个事,return会被编译器重写成SetResult,所以如果我们的异步函数返回的是一个Task<int>,代码就要改成这样: using System; ...

  2. C#的多线程——使用async和await来完成异步编程(Asynchronous Programming with async and await)

    https://msdn.microsoft.com/zh-cn/library/mt674882.aspx 侵删 更新于:2015年6月20日 欲获得最新的Visual Studio 2017 RC ...

  3. Asynchronous programming with async and await (C#)

    Asynchronous Programming with async and await (C#) | Microsoft Docs https://docs.microsoft.com/en-us ...

  4. Async Programming All in One

    Async Programming All in One Async & Await Frontend (async () => { const url = "https:// ...

  5. [转] Scala Async 库 (Scala future, await, async)

    [From] https://colobu.com/2016/02/15/Scala-Async/ 在我以前的文章中,我介绍了Scala Future and Promise.Future代表一个异步 ...

  6. async await 的 实质 本质

    async await  的 实质 就是 用 “状态机” 来 取代 函数层层调用 . async await  的 本质 是 语法糖,  和 提高性能 什么的 没什么关系 . 为了避免理解歧义, 我把 ...

  7. Javascript 使用 async 声明符和 await 操作符进行异步操作

    async function 声明用于定义一个返回 AsyncFunction 对象的异步函数 await  操作符用于等待一个Promise 对象.它只能在异步函数 async function 中 ...

  8. a kind of async programming in c#, need to reference definition

    void Main() { Run d=new Run(RunHandler); IAsyncResult result= d.BeginInvoke(new AsyncCallback(CallBa ...

  9. [Javascript] Await a JavaScript Promise in an async Function with the await Operator

    The await operator is used to wait for a promise to settle. It pauses the execution of an async func ...

随机推荐

  1. Django1.3 创建项目

    经历了各种失败各种烦恼以后Django开发环境终于搭建好了! 系统环境:ubuntu12.04    Django版本1.3.1   Python版本 2.7.3 接下来就兴建一个项目练习一下 1.创 ...

  2. jquery.easyui使用详解,和遇到的问题,提供大家在使用的时候少走弯路(二)

    上次解释了几个易犯错的地方,当然对于大神们那都是小菜一碟了,今天来说说后台请求数据,分页,返回json数据 废话不多说献上代码 private string QueryList(ArrayList a ...

  3. Redmine CodeReview

    Windows Bitnami Redmine通过自带必要的RoR依赖,解决了Redmine安装困难的问题,但也带来了很多Redmine的文档无法使用到Bitnami的问题.原因应该是很多命令不在用户 ...

  4. MySQL数据库权限操作指南

    -- 创建用户 CREATE USER 'dongrichtest' IDENTIFIED BY 'dongrichtest'; -- 新增后删除需要刷新权限 FLUSH PRIVILEGES; -- ...

  5. mongodb字段类型转化

    最近在使用mongoDB, 发现mongo对字段类型的定义并不是很严格,完全依赖传入数据的类型,在加上PHP是弱类型的语言,所以难免会出现一些错误.如果预想的类型是Int型,但数据存储的是String ...

  6. nginx-1.7.9快速启动连接手机测移动端页面

    大家好,本人之前用nginx熟熟的,后来一段时间不用,当今天再次想使用的时候,发现,悲催的是,竟然忘记怎么用了!噢噢,于是乎,就从网上搜索怎样让nginx和移动端页面联系在一起测试. 可惜的是,我搜索 ...

  7. C(++) Websocket实现扫码二维码登录---GoEasy

    最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式.当这个功能完成了后,我决定将整个实现思路整理出来,方便自己以后查看也方便其他有类似需求的程序猿些. 要实现扫码登录我们需要 ...

  8. 各数据库查询前N条记录的SQL语句

    sql在不同数据库查询前几条数据 1. ORACLE SELECT * FROM TABLE_NAME WHERE ROWNUM <= N;  HQL: from table_name t or ...

  9. 接口 Post

    public static StringBuilder HttpPost(string Url, byte[] Postdata, string i) { StringBuilder content ...

  10. Linux 下编译安装MySQL

    最近在研究Mysql,当然先要把它安装在机器上才行呀.记录下操作,加深记忆,也供以后参考. 准备工作: Linux版本:Redhat Linux 6.4 Mysql版本(安装包):mysql-5.6. ...