欧迪哥哥 發表於 2024-11-1 09:16:00

深入解析C#异步编程:await 关键字背后的实现原理

<h1 id="c-异步编程中-await-实现原理详解">C# 异步编程中 <code>await</code> 实现原理详解</h1>
<p>在C#中,<code>async</code> 和 <code>await</code> 关键字用于编写异步代码。本文将详细介绍 <code>await</code> 的实现原理,包括状态机的生成、回调函数的注册和触发等关键步骤。</p>
<h2 id="1-异步方法的基本概念">1. 异步方法的基本概念</h2>
<p>在C#中,<code>async</code> 关键字标记一个方法为异步方法,而 <code>await</code> 关键字用于等待一个异步操作完成。异步方法可以提高程序的响应性和性能,特别是在处理I/O操作和网络请求时。</p>
<h2 id="2-示例异步方法">2. 示例异步方法</h2>
<p>我们以一个简单的异步方法为例,来详细解释 <code>await</code> 的实现原理。</p>
<pre><code class="language-csharp">public class Example
{
    public async Task&lt;int&gt; CalculateAsync()
    {
      int a = await Task.Run(() =&gt; 10);
      int b = await Task.Run(() =&gt; 20);
      return a + b;
    }
}
</code></pre>
<h2 id="3-编译器生成的状态机">3. 编译器生成的状态机</h2>
<p>编译器会为每个异步方法生成一个状态机。状态机是一个结构体,包含了异步方法的所有局部变量和状态信息。</p>
<h3 id="编译器生成的状态机类">编译器生成的状态机类</h3>
<pre><code class="language-csharp">public class Example
{
    public Task&lt;int&gt; CalculateAsync()
    {
      &lt;CalculateAsync&gt;d__0 stateMachine = new &lt;CalculateAsync&gt;d__0();
      stateMachine.&lt;&gt;4__this = this;
      stateMachine.&lt;&gt;t__builder = AsyncTaskMethodBuilder&lt;int&gt;.Create();
      stateMachine.&lt;&gt;1__state = -1;
      stateMachine.&lt;&gt;t__builder.Start(ref stateMachine);
      return stateMachine.&lt;&gt;t__builder.Task;
    }

   
   
    private struct &lt;CalculateAsync&gt;d__0 : IAsyncStateMachine
    {
      public int &lt;&gt;1__state;
      public AsyncTaskMethodBuilder&lt;int&gt; &lt;&gt;t__builder;
      public Example &lt;&gt;4__this;
      public int &lt;a&gt;5__1;
      public TaskAwaiter&lt;int&gt; &lt;&gt;u__1;

      private void MoveNext()
      {
            int num = &lt;&gt;1__state;
            try
            {
                TaskAwaiter&lt;int&gt; awaiter;
                switch (num)
                {
                  case 0:
                        goto TR_0000;
                  case 1:
                        &lt;&gt;1__state = -1;
                        awaiter = &lt;&gt;u__1;
                        &lt;&gt;u__1 = default(TaskAwaiter&lt;int&gt;);
                        goto TR_0001;
                  case 2:
                        &lt;&gt;1__state = -1;
                        break;
                  default:
                        &lt;&gt;1__state = 0;
                        awaiter = Task.Run&lt;int&gt;(() =&gt; 10).GetAwaiter();
                        if (!awaiter.IsCompleted)
                        {
                            num = (&lt;&gt;1__state = 0);
                            &lt;&gt;u__1 = awaiter;
                            &lt;&gt;t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
                            return;
                        }
                        goto TR_0000;
                }
                TR_0000:
                &lt;a&gt;5__1 = awaiter.GetResult();
                awaiter = Task.Run&lt;int&gt;(() =&gt; 20).GetAwaiter();
                if (!awaiter.IsCompleted)
                {
                  num = (&lt;&gt;1__state = 1);
                  &lt;&gt;u__1 = awaiter;
                  &lt;&gt;t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
                  return;
                }
                TR_0001:
                int b = awaiter.GetResult();
                int result = &lt;a&gt;5__1 + b;
                &lt;&gt;1__state = -2;
                &lt;&gt;t__builder.SetResult(result);
            }
            catch (Exception exception)
            {
                &lt;&gt;1__state = -2;
                &lt;&gt;t__builder.SetException(exception);
            }
      }

      
      private void SetStateMachine(IAsyncStateMachine stateMachine)
      {
      }
    }
}
</code></pre>
<h2 id="4-实现流程详解">4. 实现流程详解</h2>
<h3 id="初始化状态机">初始化状态机</h3>
<p>在 <code>CalculateAsync</code> 方法中,创建状态机实例 <code>&lt;CalculateAsync&gt;d__0</code>。</p>
<pre><code class="language-csharp">&lt;CalculateAsync&gt;d__0 stateMachine = new &lt;CalculateAsync&gt;d__0();
stateMachine.&lt;&gt;4__this = this;
stateMachine.&lt;&gt;t__builder = AsyncTaskMethodBuilder&lt;int&gt;.Create();
stateMachine.&lt;&gt;1__state = -1;
</code></pre>
<ul>
<li><code>&lt;&gt;4__this</code>:指向当前实例,即 <code>Example</code> 类的实例。</li>
<li><code>&lt;&gt;t__builder</code>:创建 <code>AsyncTaskMethodBuilder&lt;int&gt;</code> 实例,用于管理任务的生命周期。</li>
<li><code>&lt;&gt;1__state</code>:初始化状态为 <code>-1</code>,表示方法尚未开始执行。</li>
</ul>
<h3 id="开始执行">开始执行</h3>
<p>调用 <code>Start</code> 方法开始执行异步方法。<code>Start</code> 方法会调用状态机的 <code>MoveNext</code> 方法。</p>
<pre><code class="language-csharp">stateMachine.&lt;&gt;t__builder.Start(ref stateMachine);
</code></pre>
<h3 id="执行方法体">执行方法体</h3>
<p>在 <code>MoveNext</code> 方法中,根据当前状态 <code>&lt;&gt;1__state</code> 执行相应的代码。</p>
<pre><code class="language-csharp">private void MoveNext()
{
    int num = &lt;&gt;1__state;
    try
    {
      TaskAwaiter&lt;int&gt; awaiter;
      switch (num)
      {
            // 处理不同的状态
      }
    }
    catch (Exception exception)
    {
      &lt;&gt;1__state = -2;
      &lt;&gt;t__builder.SetException(exception);
    }
}
</code></pre>
<h3 id="遇到-await">遇到 <code>await</code></h3>
<p>遇到第一个 <code>await</code> 关键字时,调用 <code>Task.Run(() =&gt; 10).GetAwaiter()</code> 获取 <code>Awaiter</code> 对象。</p>
<pre><code class="language-csharp">awaiter = Task.Run&lt;int&gt;(() =&gt; 10).GetAwaiter();
</code></pre>
<ul>
<li>检查 <code>awaiter.IsCompleted</code>,如果任务已经完成,直接调用 <code>awaiter.GetResult()</code> 获取结果。</li>
<li>如果任务未完成,记录当前状态 <code>&lt;&gt;1__state</code>,保存 <code>awaiter</code> 对象,并调用 <code>&lt;&gt;t__builder.AwaitUnsafeOnCompleted</code> 注册回调。</li>
</ul>
<pre><code class="language-csharp">if (!awaiter.IsCompleted)
{
    num = (&lt;&gt;1__state = 0);
    &lt;&gt;u__1 = awaiter;
    &lt;&gt;t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
    return;
}
</code></pre>
<h3 id="注册回调">注册回调</h3>
<p><code>AwaitUnsafeOnCompleted</code> 方法会注册一个回调,当任务完成时,回调会被触发。</p>
<pre><code class="language-csharp">public void AwaitUnsafeOnCompleted&lt;TAwaiter, TStateMachine&gt;(ref TAwaiter awaiter, ref TStateMachine stateMachine)
    where TAwaiter : ICriticalNotifyCompletion
    where TStateMachine : IAsyncStateMachine
{
    awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
}
</code></pre>
<ul>
<li><code>awaiter.UnsafeOnCompleted</code> 方法注册一个回调函数,该回调函数会在任务完成时被触发。</li>
<li><code>stateMachine.MoveNext</code> 是一个委托,指向状态机的 <code>MoveNext</code> 方法。</li>
</ul>
<h3 id="任务完成">任务完成</h3>
<p>当任务完成时,回调会被触发,重新调用 <code>MoveNext</code> 方法,恢复异步方法的执行。</p>
<pre><code class="language-csharp">public void OnCompleted(Action continuation)
{
    task.ContinueWith(_ =&gt; continuation(), TaskScheduler.Default);
}
</code></pre>
<h3 id="继续执行">继续执行</h3>
<p>从上次暂停的地方继续执行方法体。</p>
<pre><code class="language-csharp">TR_0000:
&lt;a&gt;5__1 = awaiter.GetResult();
awaiter = Task.Run&lt;int&gt;(() =&gt; 20).GetAwaiter();
if (!awaiter.IsCompleted)
{
    num = (&lt;&gt;1__state = 1);
    &lt;&gt;u__1 = awaiter;
    &lt;&gt;t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
    return;
}
</code></pre>
<ul>
<li>遇到第二个 <code>await</code> 关键字时,重复上述步骤。</li>
</ul>
<h3 id="方法完成">方法完成</h3>
<p>当所有异步操作完成并计算出结果后,设置状态 <code>&lt;&gt;1__state</code> 为 <code>-2</code>,表示方法已经完成。</p>
<pre><code class="language-csharp">int b = awaiter.GetResult();
int result = &lt;a&gt;5__1 + b;
&lt;&gt;1__state = -2;
&lt;&gt;t__builder.SetResult(result);
</code></pre>
<ul>
<li>调用 <code>&lt;&gt;t__builder.SetResult</code> 设置任务的结果。</li>
<li>如果在执行过程中抛出异常,捕获异常并调用 <code>&lt;&gt;t__builder.SetException</code> 设置任务的异常。</li>
</ul>
<pre><code class="language-csharp">catch (Exception exception)
{
    &lt;&gt;1__state = -2;
    &lt;&gt;t__builder.SetException(exception);
}
</code></pre>
<h2 id="5-深入理解-asynctaskmethodbuilder">5. 深入理解 <code>AsyncTaskMethodBuilder</code></h2>
<p><code>AsyncTaskMethodBuilder</code> 是一个辅助类,用于构建和管理异步方法的任务。它提供了以下方法:</p>
<ul>
<li><code>Create</code>:创建一个新的 <code>AsyncTaskMethodBuilder</code> 实例。</li>
<li><code>Start</code>:开始执行异步方法,调用状态机的 <code>MoveNext</code> 方法。</li>
<li><code>AwaitUnsafeOnCompleted</code>:注册回调函数,当任务完成时触发回调。</li>
<li><code>SetResult</code>:设置任务的结果。</li>
<li><code>SetException</code>:设置任务的异常。</li>
</ul>
<h3 id="asynctaskmethodbuilder-的内部实现"><code>AsyncTaskMethodBuilder</code> 的内部实现</h3>
<p><code>AsyncTaskMethodBuilder</code> 内部维护了一个 <code>Task</code> 对象,用于表示异步操作的结果。当异步方法完成时,<code>SetResult</code> 方法会设置任务的结果,<code>SetException</code> 方法会设置任务的异常。</p>
<pre><code class="language-csharp">public struct AsyncTaskMethodBuilder&lt;TResult&gt;
{
    private Task&lt;TResult&gt; task;

    public static AsyncTaskMethodBuilder&lt;TResult&gt; Create()
    {
      return new AsyncTaskMethodBuilder&lt;TResult&gt;(new Task&lt;TResult&gt;());
    }

    private AsyncTaskMethodBuilder(Task&lt;TResult&gt; task)
    {
      this.task = task;
    }

    public void Start&lt;TStateMachine&gt;(ref TStateMachine stateMachine)
      where TStateMachine : IAsyncStateMachine
    {
      stateMachine.MoveNext();
    }

    public void AwaitOnCompleted&lt;TAwaiter, TStateMachine&gt;(ref TAwaiter awaiter, ref TStateMachine stateMachine)
      where TAwaiter : INotifyCompletion
      where TStateMachine : IAsyncStateMachine
    {
      awaiter.OnCompleted(stateMachine.MoveNext);
    }

    public void AwaitUnsafeOnCompleted&lt;TAwaiter, TStateMachine&gt;(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&lt;TResult&gt; Task =&gt; task;
}
</code></pre>
<h2 id="6-异步方法的生命周期">6. 异步方法的生命周期</h2>
<p>异步方法的生命周期可以分为以下几个阶段:</p>
<ol>
<li><strong>初始化</strong>:创建状态机实例,初始化状态和任务构建器。</li>
<li><strong>开始执行</strong>:调用 <code>Start</code> 方法开始执行异步方法。</li>
<li><strong>执行方法体</strong>:在 <code>MoveNext</code> 方法中,根据当前状态执行相应的代码。</li>
<li><strong>遇到 <code>await</code></strong>:检查任务是否完成,如果未完成则注册回调并暂停方法执行。</li>
<li><strong>任务完成</strong>:回调被触发,重新调用 <code>MoveNext</code> 方法,恢复异步方法的执行。</li>
<li><strong>方法完成</strong>:所有异步操作完成,设置任务的结果或异常。</li>
</ol>
<h2 id="7-异步方法的优势">7. 异步方法的优势</h2>
<p>使用 <code>async</code> 和 <code>await</code> 编写的异步方法有以下优势:</p>
<ul>
<li><strong>提高响应性</strong>:异步方法不会阻塞主线程,应用程序可以继续响应用户的输入和其他事件。</li>
<li><strong>提高性能</strong>:异步方法可以并发执行多个任务,充分利用系统资源。</li>
<li><strong>简化代码</strong>:异步方法的代码结构类似于同步方法,易于理解和维护。</li>
</ul>
<h2 id="8-异步方法的注意事项">8. 异步方法的注意事项</h2>
<p>尽管 <code>async</code> 和 <code>await</code> 提供了许多优势,但在使用时也需要注意以下几点:</p>
<ul>
<li><strong>避免 <code>async void</code></strong>:<code>async void</code> 方法主要用于事件处理程序,其他情况下应避免使用,因为它无法被等待,并且异常处理较为困难。</li>
<li><strong>异常处理</strong>:异步方法中的异常会被包装在 <code>AggregateException</code> 中,需要特殊处理。</li>
<li><strong>资源管理</strong>:异步方法中使用 <code>using</code> 语句时,需要注意 <code>Dispose</code> 方法的调用时机。</li>
</ul>
<h2 id="9-完整的流程图">9. 完整的流程图</h2>
<p>为了更好地理解这个过程,可以用流程图来展示:<br>
<img src="https://img2024.cnblogs.com/blog/2696180/202410/2696180-20241031203644865-1444475892.png" alt="" loading="lazy"></p>
<h2 id="总结">总结</h2>
<p>通过上述详细的解释和示例代码,我们可以总结出以下几点:</p>
<ol>
<li><strong>异步方法的基本概念</strong>:<code>async</code> 和 <code>await</code> 关键字用于编写异步代码。</li>
<li><strong>状态机的生成</strong>:编译器为每个异步方法生成一个状态机,包含所有局部变量和状态信息。</li>
<li><strong><code>MoveNext</code> 方法的执行</strong>:<code>MoveNext</code> 方法是状态机的核心,负责管理和执行异步操作。</li>
<li><strong>回调函数的注册和触发</strong>:
<ul>
<li>当遇到 <code>await</code> 关键字时,编译器会生成代码来检查任务是否已经完成。</li>
<li>如果任务未完成,注册回调并暂停方法执行。</li>
<li>当任务完成时,回调函数会被触发,重新调用状态机的 <code>MoveNext</code> 方法,从而恢复异步方法的执行。</li>
</ul>
</li>
<li><strong><code>AwaitUnsafeOnCompleted</code> 方法的作用</strong>:在任务完成时注册一个回调函数,回调函数会在任务完成后被触发,从而恢复异步方法的执行。</li>
</ol>
<h2 id="希望这些解释能帮助你更好地理解-await-实现原理如果你还有任何疑问请随时提问详情请看原帖详情官网讲解连接">希望这些解释能帮助你更好地理解 <code>await</code> 实现原理。如果你还有任何疑问,请随时提问!<br>
详情请看:<br>
原帖详情<br>
官网讲解连接</h2>
<p>希望这篇文章对你有所帮助!如果你有任何进一步的问题或需要更多的细节,请告诉我。</p><br><br>
来源:https://www.cnblogs.com/Bob-luo/p/18518463
頁: [1]
查看完整版本: 深入解析C#异步编程:await 关键字背后的实现原理