15.5.4 【Task实现细节】一个入口搞定一切
如果你反编译过异步方法(我非常希望你会这么做),会看到状态机中的 MoveNext() 方法 非常长,变化非常快,像是一个计算有多少 await 表达式的函数。它包含原始方法中的所有逻辑, 和处理所有状态变换所需要的芭蕾舞步 ① ,以及用来处理整个结果或异常的包装代码。
在手动编写异步代码时,你通常会将后续操作分散到多个方法内:在一个方法内开始,然后 在另一个方法内继续,并且可能在第三个方法中结束。但这样很难处理循环等流控制,对C#编译 器来说更是不可能的。这和生成代码的可读性差是两码事。状态机具有单独的入口,即 MoveNext() 方法。该方法在一开始便投入使用,并且可用于所有 await 表达式的后续操作。每当调用 MoveNext() 方法时,状态机就会通过 state 字段计算出方法要跳转到的位置。在准备计算结果 时,则跳转到方法的逻辑起始位置或 await 表达式的末尾。每个状态机只执行一次操作。实际上, 在方法内部存在一个基于 state 的 switch 语句,每种情况都具有包含不同标签的对应 goto 语句。
MoveNext() 方法一般为以下形式:
void IAsyncStateMachine.MoveNext()
{
//对于声明返回Task<int>的异步方法
int result = default(int);
try
{
bool doFinallyBodies = true;
switch (state)
{
//跳转到正确的位置
}
//方法主体
}
catch (Exception e)
{
state = -;
builder.SetException(e);
return;
}
state = -;
builder.SetResult(result);
}
初始状态始终为 -1 ,方法执行时状态也是 -1 (与等待时被暂停相反)。非负值均表示一个后 续操作的目标。状态机在结束时状态为 -2 。在调试配置下创建的状态机中,你会看到一个指向 -3 状态的引用——此状态是我们未曾预料到的。退化的 switch 语句会导致糟糕的调试体验,而 -3 状态即是为避免该退化语句的出现而存在的。
在方法执行过程中,在原始异步方法的 return 语句处,会设置 result 变量。然而在到达方 法的逻辑末尾时,将其用于 builder.SetResult() 的调用。即使是非泛型的 AysncTask MethodBuilder 和 AsyncVoidMethodBuilder 类型,也包含 SetResult() 方法。前者表示对 于从骨架方法返回的任务来说,该方法已经完成;后者则表示原始的 SynchronizationContext 已经完成。(异常会以同样的方式向原始的 SynchronizationContext 传播。这是一种相当丑陋 的跟踪方式,但却对必须使用 void 方法的场景提供了一种解决方案。)
doFinallyBodies 变量用于计算执行过程离开 try 块的作用域时,原始代码中的 finally 块(包括 using 或 foreach 语句中的隐式 finally 块)是否应该执行。理论上,只有以正常方式 离开 try 块的作用域时,我们才希望执行 finally 块。如果我们只是从之前为awaiter附加了后续 操作的方法中返回,由于该方法逻辑上已经“暂停”了,因此我们不希望再执行 finally 块。 finally 块均位于相关的 try 块之后,并出现在代码方法部分的 Main 主体中。
从原始异步方法的角度来看,大多方法体都是可识别的。当然,你需要习惯于所有局部变量 都作为状态机的实例变量,但这并不是什么难事。正如你所想的那样,棘手之处是 await 表达式。
15.5.4 【Task实现细节】一个入口搞定一切的更多相关文章
- Jquery一个slideToggle搞定div的隐藏与显示
Jquery一个slideToggle搞定div的隐藏与显示 <!DOCTYPE html> <html> <head> <script src=" ...
- 将你的前端应用打包成docker镜像并部署到服务器?仅需一个脚本搞定
1.前言 前段时间,自己搞了个阿里云的服务器.想自己在上面折腾,但是不想因为自己瞎折腾而污染了现有的环境.毕竟,现在的阿里云已经没有免费的快照服务了.要想还原的话,最简单的办法就是重新装系统.而一旦重 ...
- 一个类搞定UIScrollView那些事儿
前言 UIScrollView可以说是我们在日常编程中使用频率最多.扩展性最好的一个类,根据不同的需求和设计,我们都能玩出花来,当然有一些需求是大部分应用通用的,今天就聊一下以下需求,在一个categ ...
- 一个包搞定中文数据集: datasetstore
工作中,总是要使用各种中文数据集,每次使用数据集都要花费不少的时间进行寻找,写预处理代码,结合不同的模型和框架做出相应的处理.有的时候好不容易找到合适的数据集,但是却因为网络问题,无法下载,下载了很长 ...
- 如何让两个div在同一行显示?一个float搞定
最近在学习div和css,遇到了一些问题也解决了很多以前以为很难搞定的问题.比如:如何让两个div显示在同一行呢?(不是用table表格,table对SE不太友好)其实,<div> 是一个 ...
- iOS之下拉放大,上推缩小,一个方法搞定
先来看看效果吧. 讲讲大概的实现思路:1、创建头部的视图和tableview,需要注意的是tableview要设置contentInset,contentInsent 的顶部要和头部视图的背景图的高度 ...
- 收不到Win10正式版预订通知?一个批处理搞定
目前,已经有不少Win7.Win8.1用户在系统右下角收到Win10正式版的预订提示窗口.点击接受预订后,系统会将Win10正式版所需的安装文件提前下载好,7月29日正式发布的时候,就可以第一时间升级 ...
- 一个类搞定UIScrollView那些事
前言 UIScrollView可以说是我们在日常编程中使用频率最多.扩展性最好的一个类,根据不同的需求和设计,我们都能玩出花来,当然有一些需求是大部分应用通用的,今天就聊一下以下需求,在一个categ ...
- H5 拖拽,一个函数搞定,直接指定对象设置可拖拽
页面上,弹个小窗体,想让它可以拖拽,又不想 加载一堆js,就简单的能让他可以拖动? 嗯,下面有这样一个函数,调用下就好了! 1. 先来说说 H5的 拖拽 在 HTML5 中,拖放是标准的一部分,任何元 ...
随机推荐
- EPEL reporsitory
在centos 5上yum install git的时候报错说没有git这个package. 这是因为centos的软件策略非常保守,因为它基本就是redhat企业版的copy.所以想在centos5 ...
- [Cypress] Interact with Hidden Elements in a Cypress Test
We often only show UI elements as a result of some user interaction. Cypress detects visibility and ...
- Android游戏框架Libgdx使用入门
转载自:http://blog.csdn.net/cping1982/article/details/6176191 Libgdx作者博客:http://www.badlogicgames.com/ ...
- spark streaming updateStateByKey 使用方法
updateStateByKey 解释: 以DStream中的数据进行按key做reduce操作,然后对各个批次的数据进行累加 在有新的数据信息进入或更新时.能够让用户保持想要的不论什么状.使用这个功 ...
- 发现百度开源一个好东西,Echarts统计报表前段框架
1,如今数据越来越重要了 可是数据报表的可视化展示一直是个问题. 如今好了.有Echarts能够解决一部分数据展示的问题. http://echarts.baidu.com/index.html 类似 ...
- Android ORMLite ForeignCollection关联外部集合
<Android ORMLite ForeignCollection关联外部集合> Android ORMLite ForeignCollection关联外部集合的功能,适合层 ...
- [LeetCode][Java] Minimum Depth of Binary Tree
题目: Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the ...
- SQL Source Control for teams
You'll use SQL Source Control differently depending on which development model you're using: 不同的模式有不 ...
- logistic regression二分类算法推导
- 77.招聘信息管理 EXTJS 页面
1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8&quo ...