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. 简易Map模板

    非红黑树,排序+二分搜索,查找修改O(logN),插入删除O(N) #ifndef MAP_H #define MAP_H #include "main.h" /*-------- ...

  2. Idea Diff工具介绍

    背景 项目中有大量的数据对比工作,需要生成文件,与旧文件进行对比,自动化对比和手工对比验证,自动化对比采用java-diff工具类或者手动去除换行空格注释,进行字符串对比,手动化对比可以采用git d ...

  3. OpenMP fortran 学习

    参考自TAMU的PPThttps://people.math.umass.edu/~johnston/PHI_WG_2014/OpenMPSlides_tamu_sc.pdf 什么是OpenMP 在C ...

  4. DevExpress 动态换肤

    我们都知道Devexpress内置了很多themes,那要怎么在使用时动态更改呢. 下面是方法以: 1.如果你们已经有主题了,那就在XAML中删除类似下下面的语句. dx:ThemeManager.T ...

  5. mac 查看本机ip

    创建 ip.sh 文件,文件内容为 ifconfig -a | grep -e "inet[^6]" |\ sed -e "s/.*inet[^6][^0-9]*\([0 ...

  6. rn项目下载@ant-design/react-native时发生冲突

    rn项目,使用npm i @ant-design/react-native下载antd. 下载依赖时报错: 如果你也遇到这个问题,直接告诉你结论,那就是最新的@ant-design/react-nat ...

  7. vue 生命周期个人理解

    activated():在vue对象存活的情况下,进入当前存在activated()函数的页面时,一进入页面就触发:可用于初始化页面数据等: created():在模板渲染成html前调用,即通常初始 ...

  8. HttpWebResponse 四种accept-encoding解析(gzip, deflate, br,identity)

    HttpWebResponse 四种accept-encoding解析(gzip, deflate, br,identity[默认])            var hwrs = (HttpWebRe ...

  9. 定制个性化echarts 仪表盘

    option = { series: [ { type : "gauge", center: ["50%", "45%"], // 默认全局 ...

  10. zabbix如何修改web字体

    1.拷贝一个你喜欢的字体到zabbix-server的/usr/share/zabbix/assets/fonts目录下 2.cd  /usr/share/zabbix vim include/def ...