15.5.2 【Task实现细节】骨架方法的结构
尽管骨架方法中的代码非常简单,但它暗示了状态机的职责。代码清单15-11生成的骨架方 法如下所示:
[DebuggerStepThrough]
[AsyncStateMachine(typeof(DemoStateMachine))]
static Task<int> SumCharactersAsync(IEnumerable<char> text)
{
var machine = new DemoStateMachine();
machine.text = text;
machine.builder = AsyncTaskMethodBuilder<int>.Create();
machine.state = -;
machine.builder.Start(ref machine);
return machine.builder.Task;
}
AsyncStateMachineAttribute 类型是为 async 引入的新特性(attribute)之一。它是为工具而设计的,你自己并不会有机会消费这个特性,并且也不应该在自己的方法上应用这个特性。
我们已经在这个状态机上看到了三个字段。
一个是参数( text )。显然有多少个参数就会有多少个字段。
一个是 AsyncTaskMethodBuilder<int> 。该结构负责将状态机和骨架方法联系在一起。对于仅返回 Task 的方法,存在对应的非泛型类。对于返回 void 的方法,可以使用AsnycVoidMethodBuilder 结构。
一个是 state ,值从 -1 开始。初始值永远为 -1 ,稍后我们会介绍其他值的含义。
由于状态机是一个结构(struct), AsyncTaskMethodBuilder<int> 也是一个结构,因此我 们还没有执行任何堆分配。当然,完全可以让执行的不同调用在堆上进行分配,但有必要指出的 是,代码在尽可能地避免这么做。异步的本质意味着,如果哪个 await 表达式需要真正的等待, 你会需要很多这种值(在堆上),但代码保证了它们只会在需要的时候进行装箱。所有这些都属 于实现细节,就像堆和栈属于实现细节一样,但为了让 async 能够适用于尽可能多的场景,微软 的相关团队紧密合作,将分配降低到了绝对最小值。
对 machine.builder.Start(ref machine) 的调用非常有意思。这里使用了按引用传递, 以避免创建状态机的复本(以及builder的复本),这是出于性能和正确性两方面的考虑。编译器 非常愿意将状态机和builder视为类,因此 ref 可以在代码中自由地使用。为了使用接口,不同的 方法将builder(或awaiter)作为参数,使用泛型类型参数,并限定其实现某个接口(如对于状态 机来说就是 IAsyncStateMachine )。这样在调用接口的成员时,就不需要任何装箱了。方法的 行为描述起来非常简单——它让状态机同步地执行第一个步骤,并在方法完成时或到达需等待的 异步操作点时得以返回。
第一个步骤完成后,骨架方法将返回builder中的任务。状态机在结束时,会使用builder来设 置结果或异常。
class DecompilationSampleDecompiled
{
static void Main()
{
Task<int> task = SumCharactersAsync("test");
Console.WriteLine(task.Result);
} [DebuggerStepThrough]
[AsyncStateMachine(typeof(DemoStateMachine))]
static Task<int> SumCharactersAsync(IEnumerable<char> text)
{
var machine = new DemoStateMachine();
machine.text = text;
machine.builder = AsyncTaskMethodBuilder<int>.Create();
machine.state = -;
machine.builder.Start(ref machine);
return machine.builder.Task;
} [CompilerGenerated]
private struct DemoStateMachine : IAsyncStateMachine
{
// Fields for parameters
public IEnumerable<char> text; // Fields for local variables
public IEnumerator<char> iterator;
public char ch;
public int total;
public int unicode; // Fields for awaiters
private TaskAwaiter taskAwaiter;
private YieldAwaitable.YieldAwaiter yieldAwaiter; // Common infrastructure
public int state;
public AsyncTaskMethodBuilder<int> builder;
private object stack; void IAsyncStateMachine.MoveNext()
{
int result = default(int);
try
{
bool doFinallyBodies = true;
switch (state)
{
case -:
goto Done;
case :
goto FirstAwaitContinuation;
case :
goto SecondAwaitContinuation;
}
// Default case - first call (state is -1)
total = ;
iterator = text.GetEnumerator(); // We really want to jump straight to FirstAwaitRealContinuation, but we can't
// goto a label inside a try block...
FirstAwaitContinuation:
// foreach loop
try
{
// for/foreach loops typically have the condition at the end of the generated code.
// We want to go there *unless* we're trying to reach the first continuation.
if (state != )
{
goto LoopCondition;
}
goto FirstAwaitRealContinuation;
LoopBody:
ch = iterator.Current;
unicode = ch;
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);
total += unicode;
LoopCondition:
if (iterator.MoveNext())
{
goto LoopBody;
}
}
finally
{
if (doFinallyBodies && iterator != null)
{
iterator.Dispose();
}
} // After the loop
YieldAwaitable.YieldAwaiter localYieldAwaiter = Task.Yield().GetAwaiter();
if (localYieldAwaiter.IsCompleted)
{
goto SecondAwaitCompletion;
}
state = ;
yieldAwaiter = localYieldAwaiter;
builder.AwaitUnsafeOnCompleted(ref localYieldAwaiter, ref this);
doFinallyBodies = false;
return; SecondAwaitContinuation:
localYieldAwaiter = yieldAwaiter;
yieldAwaiter = default(YieldAwaitable.YieldAwaiter);
state = -;
SecondAwaitCompletion:
localYieldAwaiter.GetResult();
localYieldAwaiter = default(YieldAwaitable.YieldAwaiter);
result = total;
}
catch (Exception ex)
{
state = -;
builder.SetException(ex);
return;
}
Done:
state = -;
builder.SetResult(result);
} [DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine machine)
{
builder.SetStateMachine(machine);
}
}
}
15.5.2 【Task实现细节】骨架方法的结构的更多相关文章
- 15.5.3 【Task实现细节】状态机的结构
状态机的整体结构非常简单.它总是使用显式接口实现,以实现.NET 4.5引入的 IAsync StateMachine 接口,并且只包含该接口声明的两个方法,即 MoveNext 和 SetState ...
- 15.5.1【Task实现细节】 生成的代码
还在吗?我们开始吧.由于深入讲解需上百页的篇幅,因此这里我不会讲得太深.但我会提 供足够的背景知识,以有助于你对整个结构的理解.之后可通过阅读我近些年来撰写的博客文章, 来了解更加错综复杂的细节,或简 ...
- 15.5.4 【Task实现细节】一个入口搞定一切
如果你反编译过异步方法(我非常希望你会这么做),会看到状态机中的 MoveNext() 方法 非常长,变化非常快,像是一个计算有多少 await 表达式的函数.它包含原始方法中的所有逻辑, 和处理所有 ...
- C#异步和多线程以及Thread、ThreadPool、Task区别和使用方法
本文的目的是为了让大家了解什么是异步?什么是多线程?如何实现多线程?对于当前C#当中三种实现多线程的方法如何实现和使用?什么情景下选用哪一技术更好? 第一部分主要介绍在C#中异步(async/awai ...
- legend2---开发日志15(功能需求明确,设计好类和结构的好处是)
legend2---开发日志15(功能需求明确,设计好类和结构的好处是) 一.总结 一句话总结: 极快简化编程,节约大量时间 1.多个类型的物品,比如商店和寻宝的丹药,装备,特性书,英雄石等等 应该怎 ...
- .NET 基础一步步一幕幕[方法、结构、枚举]
方法.结构.枚举 方法: 将一堆代码进行重用的一种机制. 语法: [访问修饰符] 返回类型 <方法名>(参数列表){ 方法主体: } 返回值类型:如果不需要写返回值,写void 方法名:P ...
- Java字节码方法表结构深度剖析
继续上一次[https://www.cnblogs.com/webor2006/p/9459681.html]的字节码分析,这次来分析一下最为复杂的方法表的信息,如下: 而上一次分析到了属性表的位置在 ...
- Unity 游戏框架搭建 2019 (三十九、四十一) 第四章 简介&方法的结构重复问题&泛型:结构复用利器
第四章 简介 方法的结构重复问题 我们在上一篇正式整理完毕,从这一篇开始,我们要再次进入学习收集示例阶段了. 那么我们学什么呢?当然是学习设计工具,也就是在上篇中提到的关键知识点.这些关键知识点,大部 ...
- 15.5.6 【Task实现细节】跟踪栈
谈到栈帧(stack frame)时,可能会想到在方法中声明的局部变量.当然,可能还会注意到 一些隐藏的局部变量,如 foreach 循环中的迭代器.但栈上的内容不止这些,至少逻辑上是这样 . 很多 ...
随机推荐
- webpack教程——css的加载
首先要安装css的loader npm install css-loader style-loader --save-dev 然后在webpack.config.js中配置如下代码 意思是先用css- ...
- Clojure:通过ZeroMQ推送消息
通过ZeroMQ的pub/sub模式,我们可以实现发送推送消息的功能.以下为示例代码(入门可参考此文:http://www.cnblogs.com/ilovewindy/p/3984269.html) ...
- select readonly 不能看到其它选项解决方式
在html中是select readonly后.依旧能够下拉选择.不想做disabled添加隐藏域,以下提供两种解决方式 解决方式1: onfocus="this.defaultIndex= ...
- 64位oracle数据库用32位plsql developer无法连接问题(无法载入oci.dll)
在64位操作系统下安装oracle数据库,新下载了64位数据库(假设是32位数据库安装在64位的操作系统上,无论是client还是server端.都不要去选择C:\Program Files (x86 ...
- BNU 13259.Story of Tomisu Ghost 分解质因子
Story of Tomisu Ghost It is now 2150 AD and problem-setters are having a horrified time as the ghost ...
- hdoj--5233--Gunner II(map+queue&&二分)
Gunner II Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Tot ...
- Head First 设计模式 —— 策略设计模式
创建一个能够根据所传递的参数对象的不同而具有不同行为(动态绑定的多态机制)的方法,被称为策略设计模式.
- com/opensymphony/xwork2/spring/SpringObjectFactory.java:220:-1问题出现的原因及解决办法
转自:https://blog.csdn.net/shinchan_/article/details/37818927 com/opensymphony/xwork2/spring/SpringObj ...
- Promise-js异步加载解决方案
范例: var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(function(){ console.log('执行完 ...
- asp.net MVC 路由注册
1.命名空间的优先级 在路由注册时指定的命名空间比当前 ControllerBuilder 的默认命名空间具有更高的匹配优先级,但是对于这两个集合中的所有命名空间却具有相同的匹配优先级.换句话说,用于 ...