任何 await 表达式均表示执行路径的一个分支。首先,被等待的异步操作得到一个awaiter,
然后检查其 IsCompleted 属性。若返回 true ,即可立即获得结果并继续。否则,需进行以下处理。
   存储awaiter,以供后面使用。
   更新状态,以表示从哪里继续。
   为awaiter附加后续操作。
   从 MoveNext() 返回,确保不会执行任何 finally 块。
  然后,在调用后续操作时,需跳转到正确的地方,获取awaiter并重置状态,然后继续。
例如,代码清单15-11中的第一个 await 表达式即:

                await Task.Delay(unicode);

  所生成的代码如下所示:

                         TaskAwaiter localTaskAwaiter = Task.Delay(unicode).GetAwaiter();
if (localTaskAwaiter.IsCompleted)
{
goto FirstAwaitCompletion;
}
state = ;
taskAwaiter = localTaskAwaiter;
builder.AwaitUnsafeOnCompleted(ref localTaskAwaiter, ref this);
doFinallyBodies = false;
return;
FirstAwaitRealContinuation:
localTaskAwaiter = taskAwaiter;
taskAwaiter = default(TaskAwaiter);
state = -;
FirstAwaitCompletion:
localTaskAwaiter.GetResult();
localTaskAwaiter = default(TaskAwaiter);

如果等待的操作有返回值(如使用 HttpClient 分配 await client.GetStringAsync(...) 的结果),那么上述代码末尾处的 GetResult() 调用将得到该值。

AwaitUnsafeOnCompleted 方法将后续操作附加给awaiter, MoveNext() 方法开头的 switch 语句可确保再次执行 MoveNext() 时,将控制传递给 DemoAwaitContinuation 。

  说明 AwaitOnCompleted 和 AwaitUnsafeOnCompleted 在此前展示的一组接口中, IAwaiter<T> 扩展了 INotifyCompletion 及其 OnCompleted 方法,此外还扩展了 ICriticalNotifyCompletion 接口及其 UnsafeOnCompleted 方法。状态机为实现 ICriticalNotifyCompletion 的 awaiter 调用 builder.AwaitUnsafeOnCompleted ,或 为只实现 INotifyCompletion 的 awaiter 调用 builder.AwaitOnCompleted 。15.6.4节 在讨论可等待模式如何与上下文交互时,会介绍这两个调用间的区别。

  注意,编译器为awaiter消除了局部变量和实例变量,这样就可以适时进行垃圾回收。 如果单个的 await 表达式也可以像这样找到块,则生成代码在反编译模式下不会太难以阅 读。由于CLR的限制,可能会存在较多的 goto 语句(及相应的标签),但在我看来, await 模式 才是最难理解的。 还有一个概念必须加以解释,那就是状态机中神秘的 stack 变量。

15.5.5 【Task实现细节】围绕 await 表达式的控制的更多相关文章

  1. 15.5.3 【Task实现细节】状态机的结构

    状态机的整体结构非常简单.它总是使用显式接口实现,以实现.NET 4.5引入的 IAsync StateMachine 接口,并且只包含该接口声明的两个方法,即 MoveNext 和 SetState ...

  2. 15.5.6 【Task实现细节】跟踪栈

    谈到栈帧(stack frame)时,可能会想到在方法中声明的局部变量.当然,可能还会注意到 一些隐藏的局部变量,如 foreach 循环中的迭代器.但栈上的内容不止这些,至少逻辑上是这样  . 很多 ...

  3. 15.5.2 【Task实现细节】骨架方法的结构

    尽管骨架方法中的代码非常简单,但它暗示了状态机的职责.代码清单15-11生成的骨架方 法如下所示: [DebuggerStepThrough] [AsyncStateMachine(typeof(De ...

  4. 15.5.1【Task实现细节】 生成的代码

    还在吗?我们开始吧.由于深入讲解需上百页的篇幅,因此这里我不会讲得太深.但我会提 供足够的背景知识,以有助于你对整个结构的理解.之后可通过阅读我近些年来撰写的博客文章, 来了解更加错综复杂的细节,或简 ...

  5. 15.5.4 【Task实现细节】一个入口搞定一切

    如果你反编译过异步方法(我非常希望你会这么做),会看到状态机中的 MoveNext() 方法 非常长,变化非常快,像是一个计算有多少 await 表达式的函数.它包含原始方法中的所有逻辑, 和处理所有 ...

  6. 理解Task和和async await

    本文将详解C#类当中的Task,以及异步函数async await和Task的关系 一.Task的前世今生 1.Thread 一开始我们需要创建线程的时候一般是通过Thread创建线程,一般常用创建线 ...

  7. 【多线程】 Task ,async ,await

    [多线程]Task ,async ,await 一. WinForm 里经常会用到多线程, 多线程的好出就不多说了,来说说多线程比较麻烦的地方 1. UI 线程与其他线程的同步,主要是 Form 和 ...

  8. C#的Task、async、await关键字

    Task,一个类,可以执行一个方法,构造函数需要传一个Action类型的委托,Action类型的委托是可以拥有多个参数,没有返回值的. Task<T> Task的泛型,构造函数传入一个Fu ...

  9. .net 关于Task.Run 和 Async await的执行顺序

    一直捋不清楚用Task.Run异步的执行关系,网上找的些说明写得也有点复杂,所以自己做实验测一下. 直接上代码 这个是加await private static void TestFun() { Co ...

随机推荐

  1. N天学习一个Linux命令之top

    用途 查看机器负载以及进程资源占用情况,linux系统性能分析工具 用法 top -hv | -abcHimMsS -d delay -n iterations -p pid [, pid ...] ...

  2. CF #321 (Div. 2) E

    用线段树维护哈希,类似于进位制的一个哈希 a[i]*p^i+a[i-1]*p^i-1... 然后,线段树存在某区间的哈希的值,对于更新,则只需提前计算出整段的哈希值即可. 判断是否相等,由于相隔为d, ...

  3. rsync + inotify 打造多server间文件实时同步

    在上篇文章ssh无password登陆server的基础之上.能够利用rsync + Inotify 在多server间实现文件自己主动同步. 例如以下測试机基于三台server做的.内网IP分别例如 ...

  4. Swift基本常识点

    import Foundation // 单行注释 // 多行注释(支持嵌套,OC是不支持的) // 常量let,初始化之后就不可改变. // 常量的具体类型可以自动识别,等号后面是什么类型,它就是什 ...

  5. 使用Linq 查询数据 构建对象 select new{}

    linq 查询数据 /// <summary> /// 汽车品牌及车型 /// </summary> /// <returns></returns> p ...

  6. Web实际应用中的编码问题

    一. JSP页面有关编码的介绍 ---->>假设不做不论什么设置,页面默认ISO-8859-1编码(Western European). ---->><%@ page c ...

  7. 在ubuntu中安装photoshop cs6

    对于很多专业的PS高手来说,真心难以找到顺手的且可以完美替代PS的软件,所以我们这里的解决办法就是用wine来安装. 虽然网上有很多的wine安装ps的方法,但是在使用过程往住会发生莫名其妙的崩溃,体 ...

  8. C++顺序表(模板总结)

    C++顺序表(模板总结) 总结: 1.模板类的实质是什么:让程序员写出和类型无关的代码 2.模板的对象时什么:方法或者类 3.是对类中的一系列操作,提供一个不固定数据类型的方法 用模板做的类的时候要指 ...

  9. Java-JRE:JRE百科

    ylbtech-Java-JRE:JRE百科 JRE是Java Runtime Environment缩写,指Java运行环境,是Sun的产品.运行JAVA程序所必须的环境的集合,包含JVM标准实现及 ...

  10. LA6878

    区间dp dp[i][j]存i->j区间的所有取值 然后枚举分割点,枚举两个存的值,分别运算存储. 看见这种不确定分割顺序,两个区间合并的情况,就要用区间dp. #include<bits ...