找回密码
 立即注册
首页 资源区 代码 一次Async/await 原理探索

一次Async/await 原理探索

垢峒 2025-5-29 10:54:11
一次Async/await 原理探索

前言

本文记录一次对 C# 中 async/await 异步编程机制的原理探索过程。异步编程的实现机制较为复杂,本文旨在通过实际代码及反编译分析,对其运行逻辑进行初步梳理和理解,供参考和学习使用。
一、前置示例

首先,通过一个简单的控制台应用演示 async/await 的基本用法:

  • 编写一个控制台应用。代码如下
    1. internal class Program
    2. {
    3.     static async Task Main(string[] args)
    4.     {
    5.         await RequestGeogle(); // 异步执行
    6.         Console.ReadLine();    // 阻塞主线程,观察结果
    7.     }
    8.     public static async Task<string> RequestGeogle()
    9.     {
    10.         using var client = new HttpClient();
    11.         var response = await client.GetAsync("https://www.google.com");       // 第一次 await:发起网络请求
    12.         var content = await response.Content.ReadAsStringAsync();             // 第二次 await:读取响应内容
    13.         return content;
    14.     }
    15. }
    复制代码
    使用反编译工具查看编译后代码的核心部分:
    1. [DebuggerStepThrough]
    2. private static void <Main>(string[] args)
    3. {
    4.     Program.Main(args).GetAwaiter().GetResult();
    5. }
    6. [DebuggerStepThrough]
    7. private static Task Main(string[] args)
    8. {
    9.     Program.<Main>d__0 stateMachine = new Program.<Main>d__0();
    10.     stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
    11.     stateMachine.args = args;
    12.     stateMachine.<>1__state = -1;
    13.     stateMachine.<>t__builder.Start(ref stateMachine);
    14.     return stateMachine.<>t__builder.Task;
    15. }
    复制代码
二、编译器所做的转换分析


  • 通过分析可以看出,编译器对异步方法进行了如下处理:
    1. 生成状态机类

    编译器会为每一个 async 方法生成一个密封状态机类(如 d__0),实现接口 IAsyncStateMachine。其命名通常不规范,目的是避免与用户代码发生命名冲突。
    1.         public interface IAsyncStateMachine
    2.         {
    3.                
    4.                 void MoveNext();
    5.                 void SetStateMachine(IAsyncStateMachine stateMachine);
    6.         }
    复制代码
  • 管理状态与上下文
    该状态机类负责保存方法的局部变量与异步状态,状态由 1__state 字段控制:

    • -1 表示初始状态;
    • 0, 1, 2... 分别表示不同 await 点;
    • `-n 表示方法已结束(成功或异常),取决于这个方法有几个await。
    同时,编译器使用 AsyncTaskMethodBuilder 来构建 Task 返回值,并负责控制方法生命周期,如启动、挂起、恢复和异常处理。

  • 启动状态机
    通过 AsyncTaskMethodBuilder.Start 启动状态机,其本质调用了:
    1. public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
    2. {
    3.     AsyncMethodBuilderCore.Start(ref stateMachine);
    4. }
    复制代码
    AsyncMethodBuilderCore 可以视作控制器,其核心职责包括:

    • 注册恢复执行的回调
    • 捕获并传播异常
    • 管理线程上下文(如 SynchronizationContext)
    • 控制异步任务的延续(continuation)执行方式

三、关键方法:MoveNext 解构

状态机的核心执行逻辑集中在 MoveNext() 方法中。该方法负责在异步操作挂起与恢复之间切换执行状态。
以下是一个简化后的 MoveNext() 解构分析:
  1. void IAsyncStateMachine.MoveNext()
  2. {
  3.     int state = this.<>1__state;
  4.     try
  5.     {
  6.         TaskAwaiter<string> awaiter1;
  7.         TaskAwaiter<string> awaiter2;
  8.         if (state != 0)
  9.         {
  10.             if (state == 1)
  11.             {
  12.                 awaiter1 = this.<>u__1;
  13.                 this.<>u__1 = default;
  14.                 this.<>1__state = -1;
  15.                 goto CONTINUE_SECOND_AWAIT;
  16.             }
  17.             awaiter2 = Program.RequestGeogle().GetAwaiter();
  18.             if (!awaiter2.IsCompleted)
  19.             {
  20.                 this.<>1__state = 0;
  21.                 this.<>u__1 = awaiter2;
  22.                 this.<>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref this);
  23.                 return;
  24.             }
  25.         }
  26.         else
  27.         {
  28.             awaiter2 = this.<>u__1;
  29.             this.<>u__1 = default;
  30.             this.<>1__state = -1;
  31.         }
  32.         awaiter2.GetResult(); // 第一次 await 完成后继续执行
  33.         awaiter1 = Program.RequestGeogle().GetAwaiter();
  34.         if (!awaiter1.IsCompleted)
  35.         {
  36.             this.<>1__state = 1;
  37.             this.<>u__1 = awaiter1;
  38.             this.<>t__builder.AwaitUnsafeOnCompleted(ref awaiter1, ref this);
  39.             return;
  40.         }
  41.     CONTINUE_SECOND_AWAIT:
  42.         string result = awaiter1.GetResult(); // 第二次 await 完成
  43.         Console.ReadLine(); // 执行剩余同步逻辑
  44.     }
  45.     catch (Exception ex)
  46.     {
  47.         this.<>1__state = -2;
  48.         this.<>t__builder.SetException(ex);
  49.         return;
  50.     }
  51.     this.<>1__state = -2;
  52.     this.<>t__builder.SetResult();
  53. }
复制代码
四、状态机制总结


  • 状态机通过 1__state 字段控制方法的进度,每个 await 语句对应一个状态。
  • 异步操作挂起时,使用 AwaitUnsafeOnCompleted 注册回调以在完成时恢复状态机执行(不涉及任何线程操作,仅挂起当前代码的上下文,空出CPU等待异步完成的回调信号)。
  • 所有本地变量和 TaskAwaiter 都被封装进状态机类中,确保挂起后上下文可以完整恢复。
  • 编译器生成的代码高度优化,最大限度保证性能,兼顾异常传播与上下文一致性。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册