The async/await keywords in C# are very much syntactical sugar that the compiler will use to generate the real code working behind async/await.

c#中的async/await关键字是语法糖,编译器使用它们来生成背后工作的真正代码.

The async/await pattern is not a core part of the language, but is instead implemented with a state machine. Each async method will be translated into a state machine and then the calling method will use this state machine to execute business logic.

async/await模式不是语言的核心部分,核心部分是通过状态机实现的。每个异步方法都将被转换为一个状态机,然后调用方法将使用这个状态机来执行业务逻辑。

示例代码

给定以下方法:

1 public async Task PrintAndWait(TimeSpan delay, int arg2)
2 {
3 Console.WriteLine("Before first delay");
4 await Task.Delay(delay);
5 Console.WriteLine("Between delays");
6 await Task.Delay(delay);
7 Console.WriteLine("After second delay");
8 }

经过编译后,上面的方法会变成如下:

 1 [AsyncStateMachine(typeof(<PrintAndWait>d__0))]
2 [DebuggerStepThrough]
3 public Task PrintAndWait(TimeSpan delay, int arg2)
4 {
5 <PrintAndWait>d__0 stateMachine = new <PrintAndWait>d__0();
6 stateMachine.<>4__this = this;
7 stateMachine.delay = delay;
8 stateMachine.arg2 = arg2;
9 stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
10 stateMachine.<>1__state = -1;
11 AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
12 <>t__builder.Start(ref stateMachine);
13 return stateMachine.<>t__builder.Task;
14 }

我们对上面的代码整理、简化成如下代码:

[AsyncStateMachine(typeof(PrintAndWaitStateMachine))]
[DebuggerStepThrough]
public Task PrintAndWait(TimeSpan delay, int arg2)
{
PrintAndWaitStateMachine stateMachine = new PrintAndWaitStateMachine()
{
Delay = delay,
Arg2 = arg2,
Builder = AsyncTaskMethodBuilder.Create(),
State = -1
};
stateMachine.Builder.Start(ref stateMachine);
return stateMachine.Builder.Task;
}

观察上面的代码,我们发现,asyncawait修饰符已被删除,方法主体已被转换为创建和启动状态机的PrintAndWaitStateMachine。同时编译器还将生成PrintAndWaitStateMachine类。生成的PrintAndWaitStateMachine类如下:

 1 [CompilerGenerated]
2 private sealed class <PrintAndWait>d__0 : IAsyncStateMachine
3 {
4 public int <>1__state;
5 public AsyncTaskMethodBuilder <>t__builder;
6 public TimeSpan delay;
7 public int arg2;
8 public C <>4__this;
9 private TaskAwaiter <>u__1;
10
11 private void MoveNext()
12 {
13 int num = <>1__state;
14 try
15 {
16 TaskAwaiter awaiter;
17 TaskAwaiter awaiter2;
18 if (num != 0)
19 {
20 if (num == 1)
21 {
22 awaiter = <>u__1;
23 <>u__1 = default(TaskAwaiter);
24 num = (<>1__state = -1);
25 goto IL_00ef;
26 }
27 Console.WriteLine("Before first delay");
28 awaiter2 = Task.Delay(delay).GetAwaiter();
29 if (!awaiter2.IsCompleted)
30 {
31 num = (<>1__state = 0);
32 <>u__1 = awaiter2;
33 <PrintAndWait>d__0 stateMachine = this;
34 <>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine);
35 return;
36 }
37 }
38 else
39 {
40 awaiter2 = <>u__1;
41 <>u__1 = default(TaskAwaiter);
42 num = (<>1__state = -1);
43 }
44 awaiter2.GetResult();
45 Console.WriteLine("Between delays");
46 awaiter = Task.Delay(delay).GetAwaiter();
47 if (!awaiter.IsCompleted)
48 {
49 num = (<>1__state = 1);
50 <>u__1 = awaiter;
51 <PrintAndWait>d__0 stateMachine = this;
52 <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
53 return;
54 }
55 goto IL_00ef;
56 IL_00ef:
57 awaiter.GetResult();
58 Console.WriteLine("After second delay");
59 }
60 catch (Exception exception)
61 {
62 <>1__state = -2;
63 <>t__builder.SetException(exception);
64 return;
65 }
66 <>1__state = -2;
67 <>t__builder.SetResult();
68 }
69
70 void IAsyncStateMachine.MoveNext()
71 {
72 //ILSpy generated this explicit interface implementation from .override directive in MoveNext
73 this.MoveNext();
74 }
75
76 [DebuggerHidden]
77 private void SetStateMachine(IAsyncStateMachine stateMachine) { }
78
79 void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
80 {
81 //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
82 this.SetStateMachine(stateMachine);
83 }
84 }

对上述代码整理、简化成如下代码:

 1 [CompilerGenerated]
2 class PrintAndWaitStateMachine : IAsyncStateMachine
3 {
4 public int State;
5 public AsyncTaskMethodBuilder Builder;
6 public TimeSpan delay;
7 public int arg2;
8
9 private TaskAwaiter _awaiter;
10
11 void IAsyncStateMachine.MoveNext()
12 {
13 int num = State;
14 try
15 {
16 TaskAwaiter awaiter;
17 TaskAwaiter awaiter2;
18 if (num != 0)
19 {
20 if (num == 1)
21 {
22 awaiter = _awaiter;
23 _awaiter = default(TaskAwaiter);
24 num = (State = -1);
25 goto IL_00ef;
26 }
27 Console.WriteLine("Before first delay");
28 awaiter2 = Task.Delay(delay).GetAwaiter();
29 if (!awaiter2.IsCompleted)
30 {
31 num = (State = 0);
32 _awaiter = awaiter2;
33 PrintAndWaitStateMachine stateMachine = this;
34 Builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine);
35 return;
36 }
37 }
38 else
39 {
40 awaiter2 = _awaiter;
41 _awaiter = default(TaskAwaiter);
42 num = (State = -1);
43 }
44 awaiter2.GetResult();
45 Console.WriteLine("Between delays");
46 awaiter = Task.Delay(delay).GetAwaiter();
47 if (!awaiter.IsCompleted)
48 {
49 num = (State = 1);
50 _awaiter = awaiter;
51 PrintAndWaitStateMachine stateMachine = this;
52 Builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
53 return;
54 }
55 goto IL_00ef;
56 IL_00ef:
57 awaiter.GetResult();
58 Console.WriteLine("After second delay");
59 }
60 catch (Exception exception)
61 {
62 State = -2;
63 Builder.SetException(exception);
64 return;
65 }
66 State = -2;
67 Builder.SetResult();
68 }
69
70 void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
71 {
72 this.Builder.SetStateMachine(stateMachine);
73 }
74 }

delayarg2参数现在是状态机类的字段,原始PrintAndWait()方法中的逻辑现在在状态机的MoveNext()方法中。没有了async修饰符,很明显就不存在IL/CLR级别的"async",编译器仅仅只是在转换代码。

状态机(The State Machine)

生成的状态机,它的工作原理是通过保存当前的方法的上下文(context)即状态(State)以便于执行完毕耗时的任务后,状态机还可以继续运行下去。

PrintAndWaitStateMachine.MoveNext()方法的内部,我们可以看见几个检查点,分别是:代表当前状态的(num)值和调用方法Builder.AwaitUnsafeOnCompleted()

 1 MoveNext()
2 {
3 int num = State;
4 try
5 {
6 TaskAwaiter awaiter;
7 TaskAwaiter awaiter2;
8 if (num != 0)
9 {
10 if (num == 1)
11 {
12 awaiter = _awaiter;
13 _awaiter = default(TaskAwaiter);
14 num = (State = -1);
15 goto IL_00ef;
16 }
17 Console.WriteLine("Before first delay");
18 awaiter2 = Task.Delay(delay).GetAwaiter();
19 if (!awaiter2.IsCompleted)
20 {
21 num = (State = 0);
22 _awaiter = awaiter2;
23 PrintAndWaitStateMachine stateMachine = this;
24 Builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine);
25 return;
26 }
27 }
28 else
29 {
30 awaiter2 = _awaiter;
31 _awaiter = default(TaskAwaiter);
32 num = (State = -1);
33 }
34 awaiter2.GetResult();
35 Console.WriteLine("Between delays");
36 awaiter = Task.Delay(delay).GetAwaiter();
37 if (!awaiter.IsCompleted)
38 {
39 num = (State = 1);
40 _awaiter = awaiter;
41 PrintAndWaitStateMachine stateMachine = this;
42 Builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
43 return;
44 }
45 goto IL_00ef;
46 IL_00ef:
47 awaiter.GetResult();
48 Console.WriteLine("After second delay");
49 }
50 catch (Exception exception)
51 {
52 State = -2;
53 Builder.SetException(exception);
54 return;
55 }
56 State = -2;
57 Builder.SetResult();
58 }

生成的代码被原始方法中的每个await关键字分割成片段。因此,该方法将一直执行到第一个等待器(await task.delay(delay)),如果这个等待器尚未完成,它将调用AwaitUnsafeOnCompleted(),传入长时间运行的任务的等待器引用和对当前状态机的引用。AwaitUnsafeOnCompleted()做的事情包括调度状态机在指定的等待器完成时继续执行下一个操作;这可以被认为类似于回调或唤醒事件。

 在AwaitUnsafeOnCompleted()方法被调用后,我们返回(或放弃对调用方法的控制权),线程被释放去做其他事情(可能是更新UI)。当等待器完成时,“Wake Up Event”被触发,MoveNext()方法再次执行,这次它已经有了一个present State,所以它将能够移动到下一个等待任务。在上面的代码中,它将遵循相同的流程来完成第二个await Task.Delay(delay)调用。
 

状态机中的状态(num/State的值)

-2

方法的结果已计算或已抛出;我们可以真正地返回(return),且再也不回来;

-1

"await Task.Delay(delay)"的起始位置。

如果任务立即完成了,或者已经完成了,就继续。

如果它还没有完成,则等待它完成,然后返回。

0~N

正整数,这些是根据原始方法中使用的await关键字的数量生成的。在上面的代码中,只使用了2个await,因此上述代码的状态机中出现了状态1和2

C# Async / Await State Machine的更多相关文章

  1. C# async await and state machine

    Async Await and the Generated StateMachine https://www.codeproject.com/Articles/535635/Async-Await-a ...

  2. Async/Await FAQ

    From time to time, I receive questions from developers which highlight either a need for more inform ...

  3. 进阶篇:以IL为剑,直指async/await

    接上篇:30分钟?不需要,轻松读懂IL,这篇主要从IL入手来理解async/await的工作原理. 先简单介绍下async/await,这是.net 4.5引入的语法糖,配合Task使用可以非常优雅的 ...

  4. C# Async&Await

    在async和await之前我们用Task来实现异步任务是这样做的: static Task<string> GetBaiduHtmlTAP() { //创建一个异步Task对象,内部封装 ...

  5. C# Under the Hood: async/await (Marko Papic)

    https://www.markopapic.com/csharp-under-the-hood-async-await/ Async and await keywords came with C# ...

  6. async/await 内幕【译文】

    C# Under the Hood: async/await 原文地址:https://www.markopapic.com/csharp-under-the-hood-async-await/ 前言 ...

  7. 【译】Async/Await(三)——Aysnc/Await模式

    原文标题:Async/Await 原文链接:https://os.phil-opp.com/async-await/#multitasking 公众号: Rust 碎碎念 翻译 by: Praying ...

  8. C# 中 async/await 调用传统 Begin/End 异步方法

    最近在改进园子的图片上传程序,希望实现用户上传图片时同时将图片文件保存在三个地方:1)服务器本地硬盘:2)又拍云:3)阿里云OSS.并且在保存时使用异步操作. 对于异步保存到本地硬盘,只需用 Stea ...

  9. Async/Await - Best Practices in Asynchronous Programming z

    These days there’s a wealth of information about the new async and await support in the Microsoft .N ...

  10. async/await 的基本实现和 .NET Core 2.1 中相关性能提升

    前言 这篇文章的开头,笔者想多说两句,不过也是为了以后再也不多嘴这样的话. 在日常工作中,笔者接触得最多的开发工作仍然是在 .NET Core 平台上,当然因为团队领导的开放性和团队风格的多样性(这和 ...

随机推荐

  1. ubuntu18.04 20.04 22.04 环境下的QGIS安装

    Linux下的QGIS安装 截至到2022年8月份,最新的qgis版本是 QGIS (3.26.x Buenos Aires) 参考网址:https://qgis.org/en/site/foruse ...

  2. Android build系统中常用“LOCAL_” 变量

    编写模块的编译文件,实际就是定义一系列以"LOCAL_"开头的编译变量,因此我们有必要弄明白这些变量的具体含义.下面是一些经常使用的LOCAL_编译变量的说明: 变量名 说明 LO ...

  3. 【python】读写文件

    1.打开文件与文件模式 1.1 文件模式 (1) 'r':读取模式(默认文件模式,显式指定读取模式的效果和不指定模式相同); (2) 'w':写入模式(如果写入时,文件不存在,会创建文件); 注1:其 ...

  4. PGI 用户手册之 Site-Specific Customization of the Compilers

    翻译自PGI Compiler User's Guide 1.6. 使用PGI编译器进行并行编程 PGI编译器支持多种样式的并行编程: 使用pgf77,pgf95,pgfortran,pgcc或pgc ...

  5. Kubernetes学习笔记(一)

    参考: kubectl Cheat Sheet | Kubernetes Kubernetes kubectl 命令表 _ Kubernetes(K8S)中文文档_Kubernetes中文社区 Pla ...

  6. Jmeter接口测试+Jmeter性能测试项目实战+Jmeter性能监控分析调优01

    Jmeter P1 性能测试关键指标 1,这些指标大家看到后有什么感觉呢?对于功能测试,一般结果就两种,成功的或者是不通过的,有问题的和没问题的,有毛病的和没毛病的,比如打开网页,能打开就打开,打不开 ...

  7. c++dump

    //Minidump.h #pragma once class CMinidump { public: CMinidump(); ~CMinidump(); static void CreateDum ...

  8. 安装.msi格式安装包

    msi格式的文件,点右键后,没有"以管理员身份运行"的菜单项,直接运行.msi文件报错:"There is a problem with this Windows Ins ...

  9. Java 04-基础 数据类型转换 自动类型转换+强制类型转换

    1.数据类型自动转换 规则1:如果一个操作数为double型,则整个表达式提示至double型 规则2:满足自动类型转换条件,   两种类型要兼容,数值类型(整数和浮点)相互兼容   目标类型取值大于 ...

  10. Luogu7912

    初中同学问我咋做,所以就写了一份题解. 先摆复杂度:均摊 \(O(n)\). 考虑,如果我们每次操作的复杂度都与输出量同阶,而输出量总量 \(O(n)\),则复杂度得到均摊. 于是我们现在要设计一个算 ...