翼度科技»论坛 编程开发 .net 查看内容

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

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
C# 异步编程中 await 实现原理详解

在C#中,async 和 await 关键字用于编写异步代码。本文将详细介绍 await 的实现原理,包括状态机的生成、回调函数的注册和触发等关键步骤。
1. 异步方法的基本概念

在C#中,async 关键字标记一个方法为异步方法,而 await 关键字用于等待一个异步操作完成。异步方法可以提高程序的响应性和性能,特别是在处理I/O操作和网络请求时。
2. 示例异步方法

我们以一个简单的异步方法为例,来详细解释 await 的实现原理。
  1. public class Example
  2. {
  3.     public async Task<int> CalculateAsync()
  4.     {
  5.         int a = await Task.Run(() => 10);
  6.         int b = await Task.Run(() => 20);
  7.         return a + b;
  8.     }
  9. }
复制代码
3. 编译器生成的状态机

编译器会为每个异步方法生成一个状态机。状态机是一个结构体,包含了异步方法的所有局部变量和状态信息。
编译器生成的状态机类
  1. public class Example
  2. {
  3.     public Task<int> CalculateAsync()
  4.     {
  5.         <CalculateAsync>d__0 stateMachine = new <CalculateAsync>d__0();
  6.         stateMachine.<>4__this = this;
  7.         stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
  8.         stateMachine.<>1__state = -1;
  9.         stateMachine.<>t__builder.Start(ref stateMachine);
  10.         return stateMachine.<>t__builder.Task;
  11.     }
  12.     [StructLayout(LayoutKind.Auto)]
  13.     [AsyncMethodBuilder(typeof(AsyncTaskMethodBuilder<int>))]
  14.     private struct <CalculateAsync>d__0 : IAsyncStateMachine
  15.     {
  16.         public int <>1__state;
  17.         public AsyncTaskMethodBuilder<int> <>t__builder;
  18.         public Example <>4__this;
  19.         public int <a>5__1;
  20.         public TaskAwaiter<int> <>u__1;
  21.         private void MoveNext()
  22.         {
  23.             int num = <>1__state;
  24.             try
  25.             {
  26.                 TaskAwaiter<int> awaiter;
  27.                 switch (num)
  28.                 {
  29.                     case 0:
  30.                         goto TR_0000;
  31.                     case 1:
  32.                         <>1__state = -1;
  33.                         awaiter = <>u__1;
  34.                         <>u__1 = default(TaskAwaiter<int>);
  35.                         goto TR_0001;
  36.                     case 2:
  37.                         <>1__state = -1;
  38.                         break;
  39.                     default:
  40.                         <>1__state = 0;
  41.                         awaiter = Task.Run<int>(() => 10).GetAwaiter();
  42.                         if (!awaiter.IsCompleted)
  43.                         {
  44.                             num = (<>1__state = 0);
  45.                             <>u__1 = awaiter;
  46.                             <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
  47.                             return;
  48.                         }
  49.                         goto TR_0000;
  50.                 }
  51.                 TR_0000:
  52.                 <a>5__1 = awaiter.GetResult();
  53.                 awaiter = Task.Run<int>(() => 20).GetAwaiter();
  54.                 if (!awaiter.IsCompleted)
  55.                 {
  56.                     num = (<>1__state = 1);
  57.                     <>u__1 = awaiter;
  58.                     <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
  59.                     return;
  60.                 }
  61.                 TR_0001:
  62.                 int b = awaiter.GetResult();
  63.                 int result = <a>5__1 + b;
  64.                 <>1__state = -2;
  65.                 <>t__builder.SetResult(result);
  66.             }
  67.             catch (Exception exception)
  68.             {
  69.                 <>1__state = -2;
  70.                 <>t__builder.SetException(exception);
  71.             }
  72.         }
  73.         [DebuggerHidden]
  74.         private void SetStateMachine(IAsyncStateMachine stateMachine)
  75.         {
  76.         }
  77.     }
  78. }
复制代码
4. 实现流程详解

初始化状态机

在 CalculateAsync 方法中,创建状态机实例 d__0。
  1. <CalculateAsync>d__0 stateMachine = new <CalculateAsync>d__0();
  2. stateMachine.<>4__this = this;
  3. stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
  4. stateMachine.<>1__state = -1;
复制代码

  • 4__this:指向当前实例,即 Example 类的实例。
  • t__builder:创建 AsyncTaskMethodBuilder 实例,用于管理任务的生命周期。
  • 1__state:初始化状态为 -1,表示方法尚未开始执行。
开始执行

调用 Start 方法开始执行异步方法。Start 方法会调用状态机的 MoveNext 方法。
  1. stateMachine.<>t__builder.Start(ref stateMachine);
复制代码
执行方法体

在 MoveNext 方法中,根据当前状态 1__state 执行相应的代码。
  1. private void MoveNext()
  2. {
  3.     int num = <>1__state;
  4.     try
  5.     {
  6.         TaskAwaiter<int> awaiter;
  7.         switch (num)
  8.         {
  9.             // 处理不同的状态
  10.         }
  11.     }
  12.     catch (Exception exception)
  13.     {
  14.         <>1__state = -2;
  15.         <>t__builder.SetException(exception);
  16.     }
  17. }
复制代码
遇到 await

遇到第一个 await 关键字时,调用 Task.Run(() => 10).GetAwaiter() 获取 Awaiter 对象。
  1. awaiter = Task.Run<int>(() => 10).GetAwaiter();
复制代码

  • 检查 awaiter.IsCompleted,如果任务已经完成,直接调用 awaiter.GetResult() 获取结果。
  • 如果任务未完成,记录当前状态 1__state,保存 awaiter 对象,并调用 t__builder.AwaitUnsafeOnCompleted 注册回调。
  1. if (!awaiter.IsCompleted)
  2. {
  3.     num = (<>1__state = 0);
  4.     <>u__1 = awaiter;
  5.     <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
  6.     return;
  7. }
复制代码
注册回调

AwaitUnsafeOnCompleted 方法会注册一个回调,当任务完成时,回调会被触发。
  1. public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
  2.     where TAwaiter : ICriticalNotifyCompletion
  3.     where TStateMachine : IAsyncStateMachine
  4. {
  5.     awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
  6. }
复制代码

  • awaiter.UnsafeOnCompleted 方法注册一个回调函数,该回调函数会在任务完成时被触发。
  • stateMachine.MoveNext 是一个委托,指向状态机的 MoveNext 方法。
任务完成

当任务完成时,回调会被触发,重新调用 MoveNext 方法,恢复异步方法的执行。
  1. public void OnCompleted(Action continuation)
  2. {
  3.     task.ContinueWith(_ => continuation(), TaskScheduler.Default);
  4. }
复制代码
继续执行

从上次暂停的地方继续执行方法体。
  1. TR_0000:
  2. <a>5__1 = awaiter.GetResult();
  3. awaiter = Task.Run<int>(() => 20).GetAwaiter();
  4. if (!awaiter.IsCompleted)
  5. {
  6.     num = (<>1__state = 1);
  7.     <>u__1 = awaiter;
  8.     <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
  9.     return;
  10. }
复制代码

  • 遇到第二个 await 关键字时,重复上述步骤。
方法完成

当所有异步操作完成并计算出结果后,设置状态 1__state 为 -2,表示方法已经完成。
  1. int b = awaiter.GetResult();
  2. int result = <a>5__1 + b;
  3. <>1__state = -2;
  4. <>t__builder.SetResult(result);
复制代码

  • 调用 t__builder.SetResult 设置任务的结果。
  • 如果在执行过程中抛出异常,捕获异常并调用 t__builder.SetException 设置任务的异常。
  1. catch (Exception exception)
  2. {
  3.     <>1__state = -2;
  4.     <>t__builder.SetException(exception);
  5. }
复制代码
5. 深入理解 AsyncTaskMethodBuilder

AsyncTaskMethodBuilder 是一个辅助类,用于构建和管理异步方法的任务。它提供了以下方法:

  • Create:创建一个新的 AsyncTaskMethodBuilder 实例。
  • Start:开始执行异步方法,调用状态机的 MoveNext 方法。
  • AwaitUnsafeOnCompleted:注册回调函数,当任务完成时触发回调。
  • SetResult:设置任务的结果。
  • SetException:设置任务的异常。
AsyncTaskMethodBuilder 的内部实现

AsyncTaskMethodBuilder 内部维护了一个 Task 对象,用于表示异步操作的结果。当异步方法完成时,SetResult 方法会设置任务的结果,SetException 方法会设置任务的异常。
  1. public struct AsyncTaskMethodBuilder<TResult>
  2. {
  3.     private Task<TResult> task;
  4.     public static AsyncTaskMethodBuilder<TResult> Create()
  5.     {
  6.         return new AsyncTaskMethodBuilder<TResult>(new Task<TResult>());
  7.     }
  8.     private AsyncTaskMethodBuilder(Task<TResult> task)
  9.     {
  10.         this.task = task;
  11.     }
  12.     public void Start<TStateMachine>(ref TStateMachine stateMachine)
  13.         where TStateMachine : IAsyncStateMachine
  14.     {
  15.         stateMachine.MoveNext();
  16.     }
  17.     public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
  18.         where TAwaiter : INotifyCompletion
  19.         where TStateMachine : IAsyncStateMachine
  20.     {
  21.         awaiter.OnCompleted(stateMachine.MoveNext);
  22.     }
  23.     public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
  24.         where TAwaiter : ICriticalNotifyCompletion
  25.         where TStateMachine : IAsyncStateMachine
  26.     {
  27.         awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
  28.     }
  29.     public void SetResult(TResult result)
  30.     {
  31.         task.SetResult(result);
  32.     }
  33.     public void SetException(Exception exception)
  34.     {
  35.         task.SetException(exception);
  36.     }
  37.     public Task<TResult> Task => task;
  38. }
复制代码
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


希望这篇文章对你有所帮助!如果你有任何进一步的问题或需要更多的细节,请告诉我。

来源:https://www.cnblogs.com/Bob-luo/p/18518463
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具