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

async_await 源码分析

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
async/await 源码解析

这篇文章主要是分析 async/await 这个语法糖,分析一下 async 和 await 是如何做到异步的。首先,我先抛出两个问题,各位可以先想一下。

  • await 之后的方法是何时执行,如何执行的?
  • 为什么 await 之后的代码会在不同的线程执行?
demo

要想知道 async/await 是怎么运行的,需要先写一个demo,然后进行一下反编译,就可以得到 async/await 编译后的代码,然后就可以开始分析了。
下面是简单使用 async/await 的demo:
  1. static async Task Main(string[] args)
  2. {
  3.        Console.WriteLine("1"+Thread.CurrentThread.ManagedThreadId.ToString());
  4.        await GetThreadID2();
  5.        Console.WriteLine("2"+Thread.CurrentThread.ManagedThreadId.ToString());
  6. }
  7. public async static Task GetThreadID2()
  8. {
  9.        Console.WriteLine("3"+Thread.CurrentThread.ManagedThreadId.ToString());
  10.        await Task.Run(() => Console.WriteLine("4"+Thread.CurrentThread.ManagedThreadId.ToString()));         
  11.        Console.WriteLine("5"+Thread.CurrentThread.ManagedThreadId.ToString());
  12. }
复制代码
然后我们来反编译一下这段代码(可以使用 dnSpy 进行反编译):
  1. namespace AsyncTest
  2. {
  3.     internal class Program
  4.     {
  5.         [DebuggerStepThrough]
  6.         private static Task Main(string[] args)
  7.         {
  8.             Program.<Main>d__0 <Main>d__ = new Program.<Main>d__0();
  9.             <Main>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
  10.             <Main>d__.args = args;
  11.             <Main>d__.<>1__state = -1;
  12.             <Main>d__.<>t__builder.Start<Program.<Main>d__0>(ref <Main>d__);
  13.             return <Main>d__.<>t__builder.Task;
  14.         }
  15.         [DebuggerStepThrough]
  16.         public static Task GetThreadID2()
  17.         {
  18.             Program.<GetThreadID2>d__2 <GetThreadID2>d__ = new Program.<GetThreadID2>d__2();
  19.             <GetThreadID2>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
  20.             <GetThreadID2>d__.<>1__state = -1;
  21.             <GetThreadID2>d__.<>t__builder.Start<Program.<GetThreadID2>d__2>(ref <GetThreadID2>d__);
  22.             return <GetThreadID2>d__.<>t__builder.Task;
  23.         }
  24.         [DebuggerStepThrough]
  25.         private static void <Main>(string[] args)
  26.         {
  27.             Program.Main(args).GetAwaiter().GetResult();
  28.         }
  29.         [CompilerGenerated]
  30.         private sealed class <Main>d__0 : IAsyncStateMachine
  31.         {
  32.             void IAsyncStateMachine.MoveNext()
  33.             {
  34.                 int num = this.<>1__state;
  35.                 try
  36.                 {
  37.                     TaskAwaiter awaiter;
  38.                     if (num != 0)
  39.                     {
  40.                         Console.WriteLine("1" + Thread.CurrentThread.ManagedThreadId.ToString());
  41.                         awaiter = Program.GetThreadID2().GetAwaiter();
  42.                         if (!awaiter.IsCompleted)
  43.                         {
  44.                             this.<>1__state = 0;
  45.                             this.<>u__1 = awaiter;
  46.                             Program.<Main>d__0 <Main>d__ = this;
  47.                             this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter, ref <Main>d__);
  48.                             return;
  49.                         }
  50.                     }
  51.                     else
  52.                     {
  53.                         awaiter = this.<>u__1;
  54.                         this.<>u__1 = default(TaskAwaiter);
  55.                         this.<>1__state = -1;
  56.                     }
  57.                     awaiter.GetResult();
  58.                     Console.WriteLine("2" + Thread.CurrentThread.ManagedThreadId.ToString());
  59.                     Console.ReadLine();
  60.                 }
  61.                 catch (Exception exception)
  62.                 {
  63.                     this.<>1__state = -2;
  64.                     this.<>t__builder.SetException(exception);
  65.                     return;
  66.                 }
  67.                 this.<>1__state = -2;
  68.                 this.<>t__builder.SetResult();
  69.             }
  70.         }
  71.         [CompilerGenerated]
  72.         [Serializable]
  73.         private sealed class <>c
  74.         {
  75.             internal void <GetThreadID2>b__2_0()
  76.             {
  77.                 Console.WriteLine("4" + Thread.CurrentThread.ManagedThreadId.ToString());
  78.             }
  79.             public static readonly Program.<>c <>9 = new Program.<>c();
  80.             public static Func<string> <>9__1_0;
  81.             public static Action <>9__2_0;
  82.         }
  83.         [CompilerGenerated]
  84.         private sealed class <GetThreadID2>d__2 : IAsyncStateMachine
  85.         {
  86.             void IAsyncStateMachine.MoveNext()
  87.             {
  88.                 int num = this.<>1__state;
  89.                 try
  90.                 {
  91.                     TaskAwaiter awaiter;
  92.                     if (num != 0)
  93.                     {
  94.                         Console.WriteLine("3" + Thread.CurrentThread.ManagedThreadId.ToString());
  95.                         awaiter = Task.Run(new Action(Program.<>c.<>9.<GetThreadID2>b__2_0)).GetAwaiter();
  96.                         if (!awaiter.IsCompleted)
  97.                         {
  98.                             this.<>1__state = 0;
  99.                             this.<>u__1 = awaiter;
  100.                             Program.<GetThreadID2>d__2 <GetThreadID2>d__ = this;
  101.                             this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<GetThreadID2>d__2>(ref awaiter, ref <GetThreadID2>d__);
  102.                             return;
  103.                         }
  104.                     }
  105.                     else
  106.                     {
  107.                         awaiter = this.<>u__1;
  108.                         this.<>u__1 = default(TaskAwaiter);
  109.                         this.<>1__state = -1;
  110.                     }
  111.                     awaiter.GetResult();
  112.                     Console.WriteLine("5" + Thread.CurrentThread.ManagedThreadId.ToString());
  113.                 }
  114.                 catch (Exception exception)
  115.                 {
  116.                     this.<>1__state = -2;
  117.                     this.<>t__builder.SetException(exception);
  118.                     return;
  119.                 }
  120.                 this.<>1__state = -2;
  121.                 this.<>t__builder.SetResult();
  122.             }
  123.             [DebuggerHidden]
  124.             void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine){}
  125.             public int <>1__state;
  126.             public AsyncTaskMethodBuilder <>t__builder;
  127.             private TaskAwaiter <>u__1;
  128.         }
  129.     }
  130. }
复制代码
上面的代码是反编译出来的所有代码,如果各位选择太长不看的话,这里还有个简化版本:
  1. private static Task Main(string[] args)
  2. <Main>d__.<>t__builder.Start<Program.<Main>d__0>(ref <Main>d__);
  3. <Main>d__0.MoveNext()
  4.     <GetThreadID2>d__.<>t__builder.Start<Program.<GetThreadID2>d__2>(ref <GetThreadID2>d__);
  5.     <GetThreadID2>d__2.MoveNext()
  6.     <GetThreadID2>d__2.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<GetThreadID2>d__2>(ref awaiter, ref <GetThreadID2>d__);
  7.     <GetThreadID2>d__2.MoveNext()
  8. <Main>d__0.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter, ref <Main>d__);
  9. <Main>d__0.MoveNext()
复制代码
入口

我们先来看一下 Main 方法。
  1. private static Task Main(string[] args)        
  2. {
  3.     Program.<Main>d__0 <Main>d__ = new Program.<Main>d__0();
  4.     <Main>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
  5.     <Main>d__.args = args;
  6.     <Main>d__.<>1__state = -1;
  7.     <Main>d__.<>t__builder.Start<Program.<Main>d__0>(ref <Main>d__);
  8.     return <Main>d__.<>t__builder.Task;
  9. }
复制代码
首先第一行,构建了一个 Program.d__0 类型实例,嗯? 这个类哪里来的,没写过呀。
哎嘿,这个类就是编译器帮我们实现的。每一个 async/await 方法,编译器都会帮我们实现一个这样的类。
简单分析一下这个方法,实例化一个 Program.d__0(), 初始化 1__state 值为-1,调用 Program.d__0() 的 Start 方法。
Start
  1. public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
  2. {
  3.     ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);
  4.     RuntimeHelpers.PrepareConstrainedRegions();
  5.     try
  6.     {
  7.         ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher);
  8.         stateMachine.MoveNext();
  9.     }
  10.     finally
  11.     {
  12.         executionContextSwitcher.Undo();
  13.     }
  14. }
复制代码
Start 代码的主要作用就是启动 stateMachine,即 stateMachine.MoveNext()。
MoveNext

MoveNext 是 async/await 里非常重要的一段代码,这个方法控制了整个异步任务的执行步骤。
我们可以将其分解成三个部分,即

  • await 之前
  • await 执行
  • await 之后的代码
    具体的分析各位请看注释。
  1. void IAsyncStateMachine.MoveNext()
  2. {
  3.     // 这里 <>1__state 之前初始化的时候是 -1,所以 num 就是-1
  4.     int num = this.<>1__state;
  5.     try
  6.     {
  7.         TaskAwaiter awaiter;
  8.         // 第一次进来,num 是 -1 所以接下来的逻辑
  9.         if (num != 0)
  10.         {
  11.             // 这里执行 await 之前的代码
  12.             Console.WriteLine("1" + Thread.CurrentThread.ManagedThreadId.ToString());
  13.             // 这里就是执行  await 方法
  14.             awaiter = Program.GetThreadID2().GetAwaiter();
  15.             // 判断是否执行完成,通常第一次进来 IsCompleted 都是 false
  16.             if (!awaiter.IsCompleted)
  17.             {
  18.                 // 修改状态,下次再执行 MoveNext 就不会继续走这段逻辑来了
  19.                 this.<>1__state = 0;
  20.                 this.<>u__1 = awaiter;
  21.                 Program.<Main>d__0 <Main>d__ = this;
  22.                 // 这里其实是往 Task 里添加了一个 任务结束时的回调,在任务结束时会再次调用 MoveNext  
  23.                 // 这就解释了为什么 await 之后的方法是另外一个线程,因为await 之后的方法是在 下一个 MoveNext 的里调用的
  24.                 // 而那个 MoveNext 是由线程池挑选一个线程进行执行的              
  25.                 this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter, ref <Main>d__);
  26.                 return;
  27.             }
  28.         }
  29.         else
  30.         {
  31.         awaiter = this.<>u__1;
  32.             this.<>u__1 = default(TaskAwaiter);
  33.             this.<>1__state = -1;
  34.         }
  35.         // GetResult 里有一个 死循环 等待结果
  36.         awaiter.GetResult();
  37.         // await 之后的方法
  38.         Console.WriteLine("2" + Thread.CurrentThread.ManagedThreadId.ToString());
  39.         Console.ReadLine();
  40.     }
  41.     catch (Exception exception)
  42.     {
  43.         this.<>1__state = -2;
  44.         this.<>t__builder.SetException(exception);
  45.         return;
  46.     }
  47.     this.<>1__state = -2;
  48.     this.<>t__builder.SetResult();
  49. }
复制代码
await 之前的代码
  1. // 这里执行 await 之前的代码
  2. Console.WriteLine("1" + Thread.CurrentThread.ManagedThreadId.ToString());
复制代码
await 之前的代码很简单,就是正常依次执行。
await 的代码

await 的代码大都以 Task 形式执行,主要分为两步。
第一步,将 Task 推入线程池中。
  1. awaiter = Task.Run(new Action(Program.<>c.<>9.<GetThreadID2>b__3_0)).GetAwaiter();
  2. public static Task Run(Action action)        
  3. {            
  4.     StackCrawlMark stackCrawlMark = StackCrawlMark.LookForMyCaller;            
  5.     return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default, TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackCrawlMark);      
  6. }
  7. internal static Task InternalStartNew(Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler, TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)        
  8. {                 
  9.     Task task = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);                     
  10.     task.ScheduleAndStart(false);            
  11.     return task;        
  12. }
  13. internal void ScheduleAndStart(bool needsProtection)
  14. {
  15.     try
  16.     {
  17.      this.m_taskScheduler.InternalQueueTask(this);
  18.     }
  19.     catch (ThreadAbortException exceptionObject){}
  20. }
  21. internal void InternalQueueTask(Task task)        
  22. {                    
  23.     this.QueueTask(task);      
  24. }
复制代码
第二步,线程池执行 Task。
  1. void IThreadPoolWorkItem.ExecuteWorkItem()        
  2. {            
  3.     this.ExecuteEntry(false);        
  4. }
  5. internal bool ExecuteEntry(bool bPreventDoubleExecution)
  6. {
  7.     if (!this.IsCancellationRequested && !this.IsCanceled)
  8.     {
  9.         this.ExecuteWithThreadLocal(ref Task.t_currentTask);
  10.     }
  11.     return true;
  12. }
  13. private void ExecuteWithThreadLocal(ref Task currentTaskSlot)
  14. {
  15.     Task task = currentTaskSlot;
  16.     TplEtwProvider log = TplEtwProvider.Log;
  17.     Guid currentThreadActivityId = default(Guid);
  18.     bool flag = log.IsEnabled();
  19.     try
  20.     {
  21.         currentTaskSlot = this;
  22.         ExecutionContext capturedContext = this.CapturedContext;
  23.         if (capturedContext == null)
  24.         {
  25.             this.Execute();
  26.         }
  27.         else
  28.         {
  29.             if (this.IsSelfReplicatingRoot || this.IsChildReplica)
  30.             {
  31.                 this.CapturedContext = Task.CopyExecutionContext(capturedContext);
  32.             }
  33.             ContextCallback contextCallback = Task.s_ecCallback;
  34.             if (contextCallback == null)
  35.             {
  36.                 contextCallback = (Task.s_ecCallback = new ContextCallback(Task.ExecutionContextCallback));
  37.             }
  38.             // 这里是执行 Task 方法的地方
  39.             ExecutionContext.Run(capturedContext, contextCallback, this, true);
  40.         }
  41.         this.Finish(true);
  42.     }
  43.     finally
  44.     {
  45.         currentTaskSlot = task;
  46.     }
  47. }
复制代码
await 之后的代码

那么 await 之后的代码是如何执行的呢?
大家回头看一下 MoveNext 方法,可以看到首次执行 MoveNext 的时候,代码的逻辑是这样的
  1. if(num!=0) -> if(!awaiter.IsCompleted) -> AwaitUnsafeOnCompleted;return;
复制代码
关键就在于 AwaitUnsafeOnCompleted 这个方法,来看一下 AwaitUnsafeOnCompleted 方法。
  1. public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
  2. {
  3.     try
  4.     {
  5.         AsyncMethodBuilderCore.MoveNextRunner runner = null;
  6.         // 这里是准备一个 任务执行完的回调,简单理解成是将 MoveNext 包装成一个 action
  7.         Action completionAction = this.m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runner);
  8.         if (this.m_coreState.m_stateMachine == null)
  9.         {
  10.             Task<TResult> task = this.Task;
  11.             this.m_coreState.PostBoxInitialization(stateMachine, runner, task);
  12.         }
  13.                 // 将 completionAction 推到 task里,具体看一下接下来的代码
  14.         awaiter.UnsafeOnCompleted(completionAction);
  15.     }
  16.     catch (Exception exception)
  17.     {
  18.         AsyncMethodBuilderCore.ThrowAsync(exception, null);
  19.     }
  20. }
  21. public void UnsafeOnCompleted(Action continuation)        
  22. {            
  23.     TaskAwaiter.OnCompletedInternal(this.m_task, continuation, true, false);        
  24. }
  25. internal static void OnCompletedInternal(Task task, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext)
  26. {
  27.     task.SetContinuationForAwait(continuation, continueOnCapturedContext, flowExecutionContext, ref stackCrawlMark);
  28. }
  29. internal void SetContinuationForAwait(Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext, ref StackCrawlMark stackMark)
  30. {
  31.      if (!this.AddTaskContinuation(continuationAction, false))
  32.      {
  33.             AwaitTaskContinuation.UnsafeScheduleAction(continuationAction, this);
  34.      }
  35. }
  36. private bool AddTaskContinuation(object tc, bool addBeforeOthers)
  37. {
  38.     //这里就把completionAction 放到了 Task 的 m_continuationObject 里
  39.     return !this.IsCompleted && ((this.m_continuationObject == null && Interlocked.CompareExchange(ref this.m_continuationObject, tc, null) == null) || this.AddTaskContinuationComplex(tc, addBeforeOthers));
  40. }
复制代码
至此为止,准备工作(将 MoveNext 放到 task 的回调里)已经做完了,接下来就是如何来触发这个方法。
简单分析一下,MoveNext 肯定是在 task 执行完触发,让我们回头看看 Task 执行的代码。会发现,哎嘿,在ExecutionContext.Run(capturedContext, contextCallback, this, true); 方法执行完后还有一句代码 this.Finish(true)。
  1. internal void Finish(bool bUserDelegateExecuted)
  2. {
  3.     this.FinishStageTwo();
  4. }
  5. internal void FinishStageTwo()
  6. {
  7.     this.FinishStageThree();
  8. }
  9. internal void FinishStageThree()
  10. {
  11.     this.FinishContinuations();
  12. }
  13. internal void FinishContinuations()
  14. {
  15.     // 获取之前注册的所有回调  最简单的就可以理解成 MoveNext
  16.     object obj = Interlocked.Exchange(ref this.m_continuationObject, Task.s_taskCompletionSentinel);
  17.     if (obj != null)
  18.     {
  19.         bool flag = (this.m_stateFlags & 134217728) == 0 && Thread.CurrentThread.ThreadState != ThreadState.AbortRequested && (this.m_stateFlags & 64) == 0;
  20.         Action action = obj as Action;
  21.         if (action != null)
  22.         {
  23.             AwaitTaskContinuation.RunOrScheduleAction(action, flag, ref Task.t_currentTask);
  24.             return;
  25.         }
  26.     }
  27. }
  28. internal static void RunOrScheduleAction(Action action, bool allowInlining, ref Task currentTask)
  29. {
  30.     // 这里调用了  MoveNextRunner.Run ,简单理解 就是  MoveNext 方法
  31.     action();
  32. }
  33. void IAsyncStateMachine.MoveNext()
  34. {   
  35.     // 此时 num 就是 0 了,就会执行 await 之后的代码了。
  36.     int num = this.<>1__state;
  37.     try
  38.     {
  39.         TaskAwaiter awaiter;
  40.         if (num != 0)
  41.         {}
  42.         else
  43.         {
  44.         awaiter = this.<>u__1;
  45.             this.<>u__1 = default(TaskAwaiter);
  46.             this.<>1__state = -1;
  47.         }
  48.         awaiter.GetResult();
  49.         Console.WriteLine("2" + Thread.CurrentThread.ManagedThreadId.ToString());
  50.         Console.ReadLine();
  51.     }
  52.     catch (Exception exception)
  53.     {
  54.     }
  55.     this.<>1__state = -2;
  56.     this.<>t__builder.SetResult();
  57. }
复制代码
这一段代码我删掉了很多内容,但大致的执行顺序就是如此,如果想要了解更多细节,可以查看 Task 这个类的源码。
总结

回头看看开头的两个问题,现在就可以回答了。

  • await 之后的方法是何时执行,如何执行的?
    await 的方法在 Task 执行完成之后,通过调用 Finish 方法执行的。
    具体的执行步骤是先将 MoveNext 方法注册到 Task 的回调里,然后在 Task 执行完后调用这个方法。
  • 为什么 await 之后的代码会在不同的线程执行?
    这个其实是因为 Task 的机制,Task 会被推到线程池里,由线程池挑选一个线程去执行,await 之后的代码其实是由这个线程去执行的,自然就跟 await 的之前的代码不是一个线程。
PS: 补一张图


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

本帖子中包含更多资源

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

x

举报 回复 使用道具