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

   此外,它还拥有大量私有或公共字段。 状态机的声明在折叠后如代码清单15-11所示:

         [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()
       {..............}
     }

  在这段代码中,我将字段分割为不同的部分。我们已经知道表示原始参数的 text 字段 是由 骨架方法设置的,而 builder 和 state 字段亦是如此,三者皆是所有状态机共享的通用基础设施。

  由于需在多次调用 MoveNext() 方法时保存变量的值,因此每个局部变量也同样拥有着自己 的字段 。有时局部变量只在两个特殊的 await 表达式之间使用,而无须保存在字段中,但就我 的经验来说,当前实现总是会将它们提升为字段。此外,这么做还可以改善调试体验,即使没有 代码再使用它们,也无须担心局部变量丢值了。

   异步方法中使用的 awaiter 如果是值类型,则每个类型都会有一个字段与之对应,而如果是 引用类型(编译时的类型),则所有 awaiter 共享一个字段。本例有两个 await 表达式,分别使 用两个不同的 awaiter 结构类型,因此有两个字段 。如果第二个 await 表达式也使用了一个 TaskAwaiter ,或者如果 TaskAwiater 和 YieldAwiter 都是类,则只会有一个字段。由于一次 只能存活一个 awaiter ,因此即使一次只能存储一个值也没关系。我们需要在多个 await 表达式 之间传播awaiter,这样就可以在操作完成时得到结果。   有关通用的基础设施字段 ,我们已经了解了其中的 state 和 builder 。 state 用于跟踪踪 迹,这样后续操作可回到代码中正确的位置。 builder 具有很多功能,包括创建骨架方法返回的 Task 和 Task<T> ,即异步方法结束时传播的任务,其内包含有正确结果。 stack 字段略微有点晦 涩。当 await 表达式作为语句的一部分出现,并需要跟踪一些额外的状态,而这些状态又没有表 示为普通的局部变量时,才会用到 stack 字段。15.5.6节将介绍一个相关示例,该示例不会用于 代码清单15-11生成的状态机中。

   编译器的所有魔法都体现在 MoveNext() 方法中,但在介绍它之前,我们先来快速浏览一下 SetStateMachine 。在每个状态机中,它都具有完全相同的实现,如下所示:

             [DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine machine)
{
builder.SetStateMachine(machine);
}

  简单来说,该方法的作用是:在builder内部,让一个已装箱状态机的复本保留有对自身的引 用。我不想深入介绍如何管理所有的装箱,你只需了解状态机可在必要时得到装箱,同时,异步 机制的各方面可保证在装箱后,还会一直使用这个已装箱的复本。这非常重要,因为我们使用的 是可变值类型(不寒而栗!)。如果允许对状态机的不同复本进行不同的修改,那么整个程序很快 就会崩溃。

  换一个角度来说(如果你开始认真思考状态机的实例变量是如何传播的,这就会变得很重 要),状态机之所以设计为 struct ,就是为了避免早期不必要的堆分配,但大多数代码都将其视 作一个类。围绕 SetStateMachine 的那些引用,让这一切正常运作。

所有代码如下:

     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.3 【Task实现细节】状态机的结构的更多相关文章

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

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

  2. 15.5.5 【Task实现细节】围绕 await 表达式的控制

    任何 await 表达式均表示执行路径的一个分支.首先,被等待的异步操作得到一个awaiter,然后检查其 IsCompleted 属性.若返回 true ,即可立即获得结果并继续.否则,需进行以下处 ...

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

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

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

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

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

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

  6. css细节复习笔记——结构与层叠

    每个合法的文档都会生成一个结构树,有了结构树元素的祖先.属性兄弟元素等等创建选择器来选择元素,这是CSS继承的核心.继承是从一个元素向后代元素传递属性值所采用的机制.面向一个元素使用哪些值时,用户代理 ...

  7. Spring task定时任务

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  8. 编程实践中C语言的一些常见细节

    对于C语言,不同的编译器采用了不同的实现,并且在不同平台上表现也不同.脱离具体环境探讨C的细节行为是没有意义的,以下是我所使用的环境,大部分内容都经过测试,且所有测试结果基于这个环境获得,为简化起见, ...

  9. Activity的task相关 详解

    task是一个具有栈结构的容器,可以放置多个Activity实例.启动一个应用,系统就会为之创建一个task,来放置根Activity:默认情况下,一个Activity启动另一个Activity时,两 ...

随机推荐

  1. data object audit

    客户提出了一个需求.想对一个表做audit. 本来这是非常简单的一个case,因为oracle自带的 audit功能就可以非常方便的实现.  实现的方式如下: BEGIN DBMS_FGA.ADD_P ...

  2. 机器学习4logistic回归

    对于线性回归.logistic回归,在以前准备学习深度学习的时候看过一点,当时的数学基础有点薄弱,虽然现在还是有点差,当时看到神经网络之后就看不下去了. 不过这次是通过python对logistic回 ...

  3. AngularJS:让submit重新生效

    当我们在html中声明了ng-app后,form的submit就会失效,必须通过angularJS来处理.如果这时还是想用普通的方式提交的话,需要修改form标签,如下所示: <form met ...

  4. ios 使用Starscream实现websocket简单例子

    调试了半天,出现 websocket is disconnected: Invalid HTTP upgrade 的错误 居然是 URL 地址写错了的原因,端口号之后还有一堆地址没有写上. 另外wss ...

  5. c++ 基于Policy 的 模板编程

    在没真正接触c++  模板编程之前.真的没有想到c++ 还能够这么用.最大的感触是:太灵活了,太强大了. 最初接触模板威力还是在Delta3d中,感觉里面的模板使用实在是灵活与方便,特别是dtAI中使 ...

  6. VC UI界面库大集合

    Guitoolkit http://www.beyondata.com/pwc.html The Ultimate Toolbox http://www.codeproject.com/KB/MFC/ ...

  7. FreeWheel基于Go的实践经验漫谈——GC是大坑(关键业务场景不用),web框架尚未统一,和c++性能相比难说

    摘自:http://www.infoq.com/cn/news/2017/06/freewheel-experience-on-go Go语言是FreeWheel公司目前主要力推的一个方向,在其看来, ...

  8. 最短路--Dijkstra&&Floyed&&SPFA

    最短路径是一个很常见的问题,这里有3种方法,可供参考. 一.Dijkstra#include<iostream> #include<cstdio> #include<cs ...

  9. 杂项-Java:EL表达式

    ylbtech-杂项-Java:EL表达式 EL(Expression Language) 是为了使JSP写起来更加简单.表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提 ...

  10. JavaScript中的+= 是什么?

    +=表示相加并赋值,“i+=5”与“i=i+5”是等效的. 类似的运算符还有-=,*=,/=. i-=5等价于i=i-5; i*=5等价于i=i*5: i/=5等价于i=i/5.