长治久安 發表於 2025-5-15 23:18:00

一次Async/await 原理探索

<h1 id="一次asyncawait-原理探索">一次Async/await 原理探索</h1>
<h2 id="前言">前言</h2>
<p>本文记录一次对 C# 中 <code>async/await</code> 异步编程机制的原理探索过程。异步编程的实现机制较为复杂,本文旨在通过实际代码及反编译分析,对其运行逻辑进行初步梳理和理解,供参考和学习使用。</p>
<h2 id="一前置示例">一、前置示例</h2>
<p>首先,通过一个简单的控制台应用演示 <code>async/await</code> 的基本用法:</p>
<ol>
<li>
<p>编写一个控制台应用。代码如下</p>
<pre><code class="language-c#">internal class Program
{
    static async Task Main(string[] args)
    {
      await RequestGeogle(); // 异步执行
      Console.ReadLine();    // 阻塞主线程,观察结果
    }

    public static async Task&lt;string&gt; RequestGeogle()
    {
      using var client = new HttpClient();
      var response = await client.GetAsync("https://www.google.com");       // 第一次 await:发起网络请求
      var content = await response.Content.ReadAsStringAsync();             // 第二次 await:读取响应内容
      return content;
    }
}

</code></pre>
<p>使用反编译工具查看编译后代码的核心部分:</p>
<pre><code class="language-c#">
private static void &lt;Main&gt;(string[] args)
{
    Program.Main(args).GetAwaiter().GetResult();
}


private static Task Main(string[] args)
{
    Program.&lt;Main&gt;d__0 stateMachine = new Program.&lt;Main&gt;d__0();
    stateMachine.&lt;&gt;t__builder = AsyncTaskMethodBuilder.Create();
    stateMachine.args = args;
    stateMachine.&lt;&gt;1__state = -1;
    stateMachine.&lt;&gt;t__builder.Start(ref stateMachine);
    return stateMachine.&lt;&gt;t__builder.Task;
}

</code></pre>
</li>
</ol>
<h2 id="二编译器所做的转换分析">二、编译器所做的转换分析</h2>
<ol>
<li>
<p>通过分析可以看出,编译器对异步方法进行了如下处理:</p>
<h3 id="1-生成状态机类">1. 生成状态机类</h3>
<p>编译器会为每一个 <code>async</code> 方法生成一个密封状态机类(如 <code>&lt;Main&gt;d__0</code>),实现接口 <code>IAsyncStateMachine</code>。其命名通常不规范,目的是避免与用户代码发生命名冲突。</p>
<pre><code>        public interface IAsyncStateMachine
        {
               
                void MoveNext();

                void SetStateMachine(IAsyncStateMachine stateMachine);
        }
</code></pre>
</li>
<li>
<p>管理状态与上下文</p>
<p>该状态机类负责保存方法的局部变量与异步状态,状态由 <code>&lt;&gt;1__state</code> 字段控制:</p>
<ul>
<li><code>-1</code> 表示初始状态;</li>
<li><code>0</code>, <code>1</code>, <code>2</code>... 分别表示不同 <code>await</code> 点;</li>
<li>`-n 表示方法已结束(成功或异常),取决于这个方法有几个await。</li>
</ul>
<p>同时,编译器使用 <code>AsyncTaskMethodBuilder</code> 来构建 <code>Task</code> 返回值,并负责控制方法生命周期,如启动、挂起、恢复和异常处理。</p>
</li>
<li>
<p>启动状态机</p>
<p>通过 <code>AsyncTaskMethodBuilder.Start</code> 启动状态机,其本质调用了:</p>
<pre><code>public void Start&lt;TStateMachine&gt;(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
    AsyncMethodBuilderCore.Start(ref stateMachine);
}
</code></pre>
<p><code>AsyncMethodBuilderCore</code> 可以视作控制器,其核心职责包括:</p>
<ul>
<li>注册恢复执行的回调</li>
<li>捕获并传播异常</li>
<li>管理线程上下文(如 <code>SynchronizationContext</code>)</li>
<li>控制异步任务的延续(continuation)执行方式</li>
</ul>
</li>
</ol>
<h2 id="三关键方法movenext-解构">三、关键方法:MoveNext 解构</h2>
<p>状态机的核心执行逻辑集中在 <code>MoveNext()</code> 方法中。该方法负责在异步操作挂起与恢复之间切换执行状态。</p>
<p>以下是一个简化后的 <code>MoveNext()</code> 解构分析:</p>
<pre><code class="language-c#">void IAsyncStateMachine.MoveNext()
{
    int state = this.&lt;&gt;1__state;
    try
    {
      TaskAwaiter&lt;string&gt; awaiter1;
      TaskAwaiter&lt;string&gt; awaiter2;

      if (state != 0)
      {
            if (state == 1)
            {
                awaiter1 = this.&lt;&gt;u__1;
                this.&lt;&gt;u__1 = default;
                this.&lt;&gt;1__state = -1;
                goto CONTINUE_SECOND_AWAIT;
            }

            awaiter2 = Program.RequestGeogle().GetAwaiter();
            if (!awaiter2.IsCompleted)
            {
                this.&lt;&gt;1__state = 0;
                this.&lt;&gt;u__1 = awaiter2;
                this.&lt;&gt;t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref this);
                return;
            }
      }
      else
      {
            awaiter2 = this.&lt;&gt;u__1;
            this.&lt;&gt;u__1 = default;
            this.&lt;&gt;1__state = -1;
      }

      awaiter2.GetResult(); // 第一次 await 完成后继续执行

      awaiter1 = Program.RequestGeogle().GetAwaiter();
      if (!awaiter1.IsCompleted)
      {
            this.&lt;&gt;1__state = 1;
            this.&lt;&gt;u__1 = awaiter1;
            this.&lt;&gt;t__builder.AwaitUnsafeOnCompleted(ref awaiter1, ref this);
            return;
      }

    CONTINUE_SECOND_AWAIT:
      string result = awaiter1.GetResult(); // 第二次 await 完成
      Console.ReadLine(); // 执行剩余同步逻辑
    }
    catch (Exception ex)
    {
      this.&lt;&gt;1__state = -2;
      this.&lt;&gt;t__builder.SetException(ex);
      return;
    }

    this.&lt;&gt;1__state = -2;
    this.&lt;&gt;t__builder.SetResult();
}

</code></pre>
<h2 id="四状态机制总结">四、状态机制总结</h2>
<ul>
<li>状态机通过 <code>&lt;&gt;1__state</code> 字段控制方法的进度,每个 <code>await</code> 语句对应一个状态。</li>
<li>异步操作挂起时,使用 <code>AwaitUnsafeOnCompleted</code> 注册回调以在完成时恢复状态机执行(不涉及任何线程操作,仅挂起当前代码的上下文,空出CPU等待异步完成的回调信号)。</li>
<li>所有本地变量和 <code>TaskAwaiter</code> 都被封装进状态机类中,确保挂起后上下文可以完整恢复。</li>
<li>编译器生成的代码高度优化,最大限度保证性能,兼顾异常传播与上下文一致性。</li>
</ul><br><br>
来源:https://www.cnblogs.com/daibitx/p/18879272
頁: [1]
查看完整版本: 一次Async/await 原理探索