深入解析C#异步编程:await 关键字背后的实现原理
C# 异步编程中 await 实现原理详解
在C#中,async 和 await 关键字用于编写异步代码。本文将详细介绍 await 的实现原理,包括状态机的生成、回调函数的注册和触发等关键步骤。
1. 异步方法的基本概念
在C#中,async 关键字标记一个方法为异步方法,而 await 关键字用于等待一个异步操作完成。异步方法可以提高程序的响应性和性能,特别是在处理I/O操作和网络请求时。
2. 示例异步方法
我们以一个简单的异步方法为例,来详细解释 await 的实现原理。
public class Example
{
    public async Task<int> CalculateAsync()
    {
        int a = await Task.Run(() => 10);
        int b = await Task.Run(() => 20);
        return a + b;
    }
}
3. 编译器生成的状态机
编译器会为每个异步方法生成一个状态机。状态机是一个结构体,包含了异步方法的所有局部变量和状态信息。
编译器生成的状态机类
public class Example
{
    public Task<int> CalculateAsync()
    {
        <CalculateAsync>d__0 stateMachine = new <CalculateAsync>d__0();
        stateMachine.<>4__this = this;
        stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
        stateMachine.<>1__state = -1;
        stateMachine.<>t__builder.Start(ref stateMachine);
        return stateMachine.<>t__builder.Task;
    }
    [StructLayout(LayoutKind.Auto)]
    [AsyncMethodBuilder(typeof(AsyncTaskMethodBuilder<int>))]
    private struct <CalculateAsync>d__0 : IAsyncStateMachine
    {
        public int <>1__state;
        public AsyncTaskMethodBuilder<int> <>t__builder;
        public Example <>4__this;
        public int <a>5__1;
        public TaskAwaiter<int> <>u__1;
        private void MoveNext()
        {
            int num = <>1__state;
            try
            {
                TaskAwaiter<int> awaiter;
                switch (num)
                {
                    case 0:
                        goto TR_0000;
                    case 1:
                        <>1__state = -1;
                        awaiter = <>u__1;
                        <>u__1 = default(TaskAwaiter<int>);
                        goto TR_0001;
                    case 2:
                        <>1__state = -1;
                        break;
                    default:
                        <>1__state = 0;
                        awaiter = Task.Run<int>(() => 10).GetAwaiter();
                        if (!awaiter.IsCompleted)
                        {
                            num = (<>1__state = 0);
                            <>u__1 = awaiter;
                            <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
                            return;
                        }
                        goto TR_0000;
                }
                TR_0000:
                <a>5__1 = awaiter.GetResult();
                awaiter = Task.Run<int>(() => 20).GetAwaiter();
                if (!awaiter.IsCompleted)
                {
                    num = (<>1__state = 1);
                    <>u__1 = awaiter;
                    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
                    return;
                }
                TR_0001:
                int b = awaiter.GetResult();
                int result = <a>5__1 + b;
                <>1__state = -2;
                <>t__builder.SetResult(result);
            }
            catch (Exception exception)
            {
                <>1__state = -2;
                <>t__builder.SetException(exception);
            }
        }
        [DebuggerHidden]
        private void SetStateMachine(IAsyncStateMachine stateMachine)
        {
        }
    }
}
4. 实现流程详解
初始化状态机
在 CalculateAsync 方法中,创建状态机实例 <CalculateAsync>d__0。
<CalculateAsync>d__0 stateMachine = new <CalculateAsync>d__0();
stateMachine.<>4__this = this;
stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
stateMachine.<>1__state = -1;
- <>4__this:指向当前实例,即- Example类的实例。
- <>t__builder:创建- AsyncTaskMethodBuilder<int>实例,用于管理任务的生命周期。
- <>1__state:初始化状态为- -1,表示方法尚未开始执行。
开始执行
调用 Start 方法开始执行异步方法。Start 方法会调用状态机的 MoveNext 方法。
stateMachine.<>t__builder.Start(ref stateMachine);
执行方法体
在 MoveNext 方法中,根据当前状态 <>1__state 执行相应的代码。
private void MoveNext()
{
    int num = <>1__state;
    try
    {
        TaskAwaiter<int> awaiter;
        switch (num)
        {
            // 处理不同的状态
        }
    }
    catch (Exception exception)
    {
        <>1__state = -2;
        <>t__builder.SetException(exception);
    }
}
遇到 await
遇到第一个 await 关键字时,调用 Task.Run(() => 10).GetAwaiter() 获取 Awaiter 对象。
awaiter = Task.Run<int>(() => 10).GetAwaiter();
- 检查 awaiter.IsCompleted,如果任务已经完成,直接调用awaiter.GetResult()获取结果。
- 如果任务未完成,记录当前状态 <>1__state,保存awaiter对象,并调用<>t__builder.AwaitUnsafeOnCompleted注册回调。
if (!awaiter.IsCompleted)
{
    num = (<>1__state = 0);
    <>u__1 = awaiter;
    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
    return;
}
注册回调
AwaitUnsafeOnCompleted 方法会注册一个回调,当任务完成时,回调会被触发。
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
    where TAwaiter : ICriticalNotifyCompletion
    where TStateMachine : IAsyncStateMachine
{
    awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
}
- awaiter.UnsafeOnCompleted方法注册一个回调函数,该回调函数会在任务完成时被触发。
- stateMachine.MoveNext是一个委托,指向状态机的- MoveNext方法。
任务完成
当任务完成时,回调会被触发,重新调用 MoveNext 方法,恢复异步方法的执行。
public void OnCompleted(Action continuation)
{
    task.ContinueWith(_ => continuation(), TaskScheduler.Default);
}
继续执行
从上次暂停的地方继续执行方法体。
TR_0000:
<a>5__1 = awaiter.GetResult();
awaiter = Task.Run<int>(() => 20).GetAwaiter();
if (!awaiter.IsCompleted)
{
    num = (<>1__state = 1);
    <>u__1 = awaiter;
    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
    return;
}
- 遇到第二个 await关键字时,重复上述步骤。
方法完成
当所有异步操作完成并计算出结果后,设置状态 <>1__state 为 -2,表示方法已经完成。
int b = awaiter.GetResult();
int result = <a>5__1 + b;
<>1__state = -2;
<>t__builder.SetResult(result);
- 调用 <>t__builder.SetResult设置任务的结果。
- 如果在执行过程中抛出异常,捕获异常并调用 <>t__builder.SetException设置任务的异常。
catch (Exception exception)
{
    <>1__state = -2;
    <>t__builder.SetException(exception);
}
5. 深入理解 AsyncTaskMethodBuilder
AsyncTaskMethodBuilder 是一个辅助类,用于构建和管理异步方法的任务。它提供了以下方法:
- Create:创建一个新的- AsyncTaskMethodBuilder实例。
- Start:开始执行异步方法,调用状态机的- MoveNext方法。
- AwaitUnsafeOnCompleted:注册回调函数,当任务完成时触发回调。
- SetResult:设置任务的结果。
- SetException:设置任务的异常。
AsyncTaskMethodBuilder 的内部实现
AsyncTaskMethodBuilder 内部维护了一个 Task 对象,用于表示异步操作的结果。当异步方法完成时,SetResult 方法会设置任务的结果,SetException 方法会设置任务的异常。
public struct AsyncTaskMethodBuilder<TResult>
{
    private Task<TResult> task;
    public static AsyncTaskMethodBuilder<TResult> Create()
    {
        return new AsyncTaskMethodBuilder<TResult>(new Task<TResult>());
    }
    private AsyncTaskMethodBuilder(Task<TResult> task)
    {
        this.task = task;
    }
    public void Start<TStateMachine>(ref TStateMachine stateMachine)
        where TStateMachine : IAsyncStateMachine
    {
        stateMachine.MoveNext();
    }
    public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : INotifyCompletion
        where TStateMachine : IAsyncStateMachine
    {
        awaiter.OnCompleted(stateMachine.MoveNext);
    }
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : ICriticalNotifyCompletion
        where TStateMachine : IAsyncStateMachine
    {
        awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
    }
    public void SetResult(TResult result)
    {
        task.SetResult(result);
    }
    public void SetException(Exception exception)
    {
        task.SetException(exception);
    }
    public Task<TResult> Task => task;
}
6. 异步方法的生命周期
异步方法的生命周期可以分为以下几个阶段:
- 初始化:创建状态机实例,初始化状态和任务构建器。
- 开始执行:调用 Start方法开始执行异步方法。
- 执行方法体:在 MoveNext方法中,根据当前状态执行相应的代码。
- 遇到 await:检查任务是否完成,如果未完成则注册回调并暂停方法执行。
- 任务完成:回调被触发,重新调用 MoveNext方法,恢复异步方法的执行。
- 方法完成:所有异步操作完成,设置任务的结果或异常。
7. 异步方法的优势
使用 async 和 await 编写的异步方法有以下优势:
- 提高响应性:异步方法不会阻塞主线程,应用程序可以继续响应用户的输入和其他事件。
- 提高性能:异步方法可以并发执行多个任务,充分利用系统资源。
- 简化代码:异步方法的代码结构类似于同步方法,易于理解和维护。
8. 异步方法的注意事项
尽管 async 和 await 提供了许多优势,但在使用时也需要注意以下几点:
- 避免 async void:async void方法主要用于事件处理程序,其他情况下应避免使用,因为它无法被等待,并且异常处理较为困难。
- 异常处理:异步方法中的异常会被包装在 AggregateException中,需要特殊处理。
- 资源管理:异步方法中使用 using语句时,需要注意Dispose方法的调用时机。
9. 完整的流程图
为了更好地理解这个过程,可以用流程图来展示:

总结
通过上述详细的解释和示例代码,我们可以总结出以下几点:
- 异步方法的基本概念:async和await关键字用于编写异步代码。
- 状态机的生成:编译器为每个异步方法生成一个状态机,包含所有局部变量和状态信息。
- MoveNext方法的执行:- MoveNext方法是状态机的核心,负责管理和执行异步操作。
- 回调函数的注册和触发:
- 当遇到 await关键字时,编译器会生成代码来检查任务是否已经完成。
- 如果任务未完成,注册回调并暂停方法执行。
- 当任务完成时,回调函数会被触发,重新调用状态机的 MoveNext方法,从而恢复异步方法的执行。
 
- 当遇到 
- AwaitUnsafeOnCompleted方法的作用:在任务完成时注册一个回调函数,回调函数会在任务完成后被触发,从而恢复异步方法的执行。
希望这些解释能帮助你更好地理解 await 实现原理。如果你还有任何疑问,请随时提问!
详情请看:https://www.cnblogs.com/Bob-luo/p/18518463
希望这篇文章对你有所帮助!如果你有任何进一步的问题或需要更多的细节,请告诉我。
深入解析C#异步编程:await 关键字背后的实现原理的更多相关文章
- C#异步编程のawait和async关键字来写异步程序
		一.await和async关键字 .Net平台不断推出了新的异步编程模型,在.net4.5中加入了关键字await和async,顾名思义,await是指方法执行可等待,即可挂起直到有结果(不是必须立即 ... 
- 深入解析js异步编程利器Generator
		我们在编写Nodejs程序时,经常会用到回调函数,在一个操作执行完成之后对返回的数据进行处理,我简单的理解它为异步编程. 如果操作很多,那么回调的嵌套就会必不可少,那么如果操作非常多,那么回调的嵌套就 ... 
- 深入解析Javascript异步编程
		这里深入探讨下Javascript的异步编程技术.(P.S. 本文较长,请准备好瓜子可乐 :D) 一. Javascript异步编程简介 至少在语言级别上,Javascript是单线程的,因此异步编程 ... 
- .Net 多线程 异步编程 Await、Async和Task
		await和async简介 await和async是在C#5中引入,并且在.NetFramewor4.5以及.NetCore中进行了支持.主要是解决性能瓶颈,并且增强系统的响应能力. msdn关于 ... 
- 一步步疑难解析 —— Python 异步编程构建博客
		声明:该项目学习资源主要来自廖雪峰的Python教程,参见 http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6 ... 
- 对于异步编程Await和Async的理解
		public class AsyncInSync { /// <summary> /// 同步代码里有异步代码 /// /// /// 结果 /// Main Thread Before ... 
- 基于纤程(Fiber)实现C++异步编程库(一):原理及示例
		纤程(Fiber)和协程(coroutine)是差不多的概念,也叫做用户级线程或者轻线程之类的.Windows系统提供了一组API用户创建和使用纤程,本文中的库就是基于这组API实现的,所以无法跨平台 ... 
- 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单
		一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两 ... 
- 转:[你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单
		本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单 async和await关键字剖析 小结 一.引言 在之前的C#基础知 ... 
- [你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单
		本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单 async和await关键字剖析 小结 一.引言 在之前的C#基础知 ... 
随机推荐
- ChatGLM
			ChatGLM: A Family of Large Language Models from GLM-130B to GLM-4 All Tools(2024.7.16) Code:https:// ... 
- Unreal使用GooglePAD生成AAB包,并加在fast-follow资源
			1.修改obbfilter,设置需要添加到obb的pak文件 2.修改项目设置,打AAB包 3.cook stage生成所有Paks文件 4.将部分pak文件拷贝到Intermediate/Andro ... 
- CKS考试心得分享
			CKS证书 考试相关 考试报名准备 CKS考试和CKA考试一样,已经开放中国大陆的考试.但区别是CKS目前没有中文题目,考试都是英文题目,唯一区别是CKS中文考试是中文老师监考,仅此而已.因此,建议C ... 
- 《黑神话:悟空》神话再现,虚幻引擎与Unity/C#谁更强?
			前言 在国产游戏领域,<黑神话:悟空>无疑是一颗耀眼的明星,以独特的艺术风格.深厚的文化底蕴以及卓越的技术表现,赢得了国内外玩家的广泛关注.然而,在这款游戏光鲜亮丽的背后,是我们开发者对技 ... 
- Hexo-GitHub部署魔改第一步-config
			Hexo-GitHub部署魔改第一步_config.yml 1. config.yml # Hexo Configuration ## Docs: https://hexo.io/docs/confi ... 
- Python if __name__ == "__main__" 解释
			一种机制,允许脚本以不同的方式运行,这取决于作为独立的程序执行还是作为模块被其他脚本导入.这种机制就是 if __name == "__main__" 其作用是控制某些代码块只在该 ... 
- 【测试平台开发】——04Flask后端api开发实战(一)
			一.测试平台开发模式 要开发一套平台有两种开发模式,一个[大而全],一个[小而简]. 说道[大而全]想到目前大型项目都使用的语言[JAVA],[小而简]想到的是[Python]语言. 重武器(大而全) ... 
- vscode 安装历史版本
			修改版本号为想要下载的版本即可 https://update.code.visualstudio.com/{版本}/win32-x64-archive/stable vscode 历史版本地址:Vis ... 
- 光影精灵10 Win1+Ubuntu18.04 双系统  踩坑记录
			前言 第二年准备报名智能车了,当然还是创意组别.刚好买了今年新出的电脑光影精灵10,我想着也给它安一个双系统.但是没想到,相比于之前那个老电脑,新电脑的新硬件和驱动问题远比老电脑麻烦的多. 在经历了一 ... 
- JAVA基础之5-函数式接口的实现
			之所以单独把这个列出来,是因为本人被一个源码给震撼了. 所以,本人目的是看看这个震撼实现,并模仿,最后把常规的实现也贴上,让读者可以看到相对完整的实现 注:本文代码基于JDK17 一.让人震撼的代码 ... 
