尽管骨架方法中的代码非常简单,但它暗示了状态机的职责。代码清单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实现细节】骨架方法的结构的更多相关文章

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

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

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

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

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

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

  4. C#异步和多线程以及Thread、ThreadPool、Task区别和使用方法

    本文的目的是为了让大家了解什么是异步?什么是多线程?如何实现多线程?对于当前C#当中三种实现多线程的方法如何实现和使用?什么情景下选用哪一技术更好? 第一部分主要介绍在C#中异步(async/awai ...

  5. legend2---开发日志15(功能需求明确,设计好类和结构的好处是)

    legend2---开发日志15(功能需求明确,设计好类和结构的好处是) 一.总结 一句话总结: 极快简化编程,节约大量时间 1.多个类型的物品,比如商店和寻宝的丹药,装备,特性书,英雄石等等 应该怎 ...

  6. .NET 基础一步步一幕幕[方法、结构、枚举]

    方法.结构.枚举 方法: 将一堆代码进行重用的一种机制. 语法: [访问修饰符] 返回类型 <方法名>(参数列表){ 方法主体: } 返回值类型:如果不需要写返回值,写void 方法名:P ...

  7. Java字节码方法表结构深度剖析

    继续上一次[https://www.cnblogs.com/webor2006/p/9459681.html]的字节码分析,这次来分析一下最为复杂的方法表的信息,如下: 而上一次分析到了属性表的位置在 ...

  8. Unity 游戏框架搭建 2019 (三十九、四十一) 第四章 简介&方法的结构重复问题&泛型:结构复用利器

    第四章 简介 方法的结构重复问题 我们在上一篇正式整理完毕,从这一篇开始,我们要再次进入学习收集示例阶段了. 那么我们学什么呢?当然是学习设计工具,也就是在上篇中提到的关键知识点.这些关键知识点,大部 ...

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

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

随机推荐

  1. pt-osc改表导致数据不一致案例分析

    2016-06-10 李丹 dba流浪猫 我们平时除了解决自己问题外,有时候也会协助圈内人士,进行一些故障排查,此案例就是帮某公司DBA进行的故障分析,因为比较典型,特分享一下,但仅仅是分享发生的过程 ...

  2. Oracle推断值为非数字

    select * from product_info t where t.contract_detailid is not null and length(translate(t.contract_d ...

  3. UVA 12683 Odd and Even Zeroes(数学—找规律)

    Time Limit: 1000 MS In mathematics, the factorial of a positive integer number n is written as n! an ...

  4. javaweb项目中获取项目名称

    request.getServletContext().getContextPath() 增加项目名称是test.那么上面的结果就是/test

  5. OC3大回调模式使用总结(三)block回调

    OC 3大回调模式使用总结(三)block回调 block 又称 代码块,闭包等 是一个匿名的函数,它能够当做一个对象来使用,仅仅只是这个对象非常特殊,是一段代码,他能够保存你写的一段预备性质代码,待 ...

  6. 在Twitter信息流中大规模应用深度学习——推文的相关度计算使用了深度学习

    我们如何对信息流进行排序? 在引入排序算法之前,信息流的组成非常简单:收集所有由你的关注对象在你最后一次登录Twitter之后发送的推文,再将它们按照时间倒序显示出来.这个看起来很简单,但要为数以亿计 ...

  7. hdu 1075(字典树)

    What Are You Talking About Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/204800 K ...

  8. poj--3187--Backward Digit Sums(dfs)

    Backward Digit Sums Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 5667   Accepted: 32 ...

  9. 前缀和&&离散化

    现在正在上课,但我还是要同步更新博文...\滑稽 先讲一个离散化,就是把几个离的特别远的数在不影响结果的情况下,变成相近的数.倒是没什么影响,但应用在数组下标的话可以节约空间.(貌似和hash有点像) ...

  10. 多线程编程和Java网络编程

    1. 线程概述 多任务处理有两种类型:基于进程.基于线程(进程是指一种“自包容”的运行程序,有自己的地址空间; 线程是进程内部单一的一个顺序控制流) 基于进程的特点是允许计算机同时运行两个或更多的程序 ...