2.调用builder的start方法,builder类型是 AsyncTaskMethodBuilder,点进去看看 builder是个什么东西。
AsyncTaskMethodBuilder
public struct AsyncTaskMethodBuilder<[Nullable(2)] TResult> { [DebuggerStepThrough] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Start<[Nullable(0)] TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { // 进去看看=>函数2 AsyncMethodBuilderCore.Start<TStateMachine>(ref stateMachine); } } //函数2 internal static class AsyncMethodBuilderCore { public static void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { if (stateMachine == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine); } Thread currentThread = Thread.CurrentThread; ExecutionContext executionContext = currentThread._executionContext; SynchronizationContext synchronizationContext = currentThread._synchronizationContext; try { //状态机调用 stateMachine.MoveNext(); } finally { if (synchronizationContext != currentThread._synchronizationContext) { currentThread._synchronizationContext = synchronizationContext; } ExecutionContext executionContext2 = currentThread._executionContext; if (executionContext != executionContext2) { ExecutionContext.RestoreChangedContextToThread(currentThread, executionContext, executionContext2); } } } }
可以看出实际上是调用了状态机的movenext方法,好的继续看。代码已经在最上面贴出来了。
movenext
梳理一下逻辑:
初始状态 num = -1 , 控制台打印 ,调用异步函数,获取awaiter ,所有可等待的对象必定可以获取awaiter,,可以查看此任务是否完成,未完成:则会调用 AwaitUnsafeOnCompleted 函数 实际上这个函数就是注册任务完成的回调函数。
AwaitUnsafeOnCompleted 等一系列方法
public struct AsyncTaskMethodBuilder<[Nullable(2)] TResult> { // 函数1 public void AwaitOnCompleted<[Nullable(0)] TAwaiter, [Nullable(0)] TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { // 调用下面的函数 AsyncTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted<TAwaiter, TStateMachine>(ref awaiter, ref stateMachine, ref this.m_task); } // 函数2 [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitUnsafeOnCompleted<[Nullable(0)] TAwaiter, [Nullable(0)] TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { AsyncTaskMethodBuilder<VoidTaskResult>.AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref awaiter, ref stateMachine, ref this.m_task); } // 函数3 internal static void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine, [NotNull] ref Task<TResult> taskField) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { IAsyncStateMachineBox stateMachineBox = AsyncTaskMethodBuilder<TResult>.GetStateMachineBox<TStateMachine>(ref stateMachine, ref taskField); AsyncTaskMethodBuilder<TResult>.AwaitUnsafeOnCompleted<TAwaiter>(ref awaiter, stateMachineBox); } }
函数1 => 函数2 => 函数 3,分别调用。
可以看到函数 3 对我们的状态机进行了包装,生成一个状态机盒子,进去看看。
GetStateMachineBox
private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(ref TStateMachine stateMachine, [NotNull] ref Task<TResult> taskField) where TStateMachine : IAsyncStateMachine { //捕捉当前环境上下文 ExecutionContext executionContext = ExecutionContext.Capture(); //根据调试的内容taskfield 为null 如果有多个await代码段 第二次movenext就会走这段逻辑 AsyncTaskMethodBuilder<TResult>.AsyncStateMachineBox<TStateMachine> asyncStateMachineBox = taskField as AsyncTaskMethodBuilder<TResult>.AsyncStateMachineBox<TStateMachine>; if (asyncStateMachineBox != null) { if (asyncStateMachineBox.Context != executionContext) { asyncStateMachineBox.Context = executionContext; } return asyncStateMachineBox; } AsyncTaskMethodBuilder<TResult>.AsyncStateMachineBox<IAsyncStateMachine> asyncStateMachineBox2 = taskField as AsyncTaskMethodBuilder<TResult>.AsyncStateMachineBox<IAsyncStateMachine>; if (asyncStateMachineBox2 != null) { if (asyncStateMachineBox2.StateMachine == null) { Debugger.NotifyOfCrossThreadDependency(); asyncStateMachineBox2.StateMachine = stateMachine; } asyncStateMachineBox2.Context = executionContext; return asyncStateMachineBox2; } Debugger.NotifyOfCrossThreadDependency(); //走以下逻辑 AsyncTaskMethodBuilder<TResult>.AsyncStateMachineBox<TStateMachine> asyncStateMachineBox3 = (AsyncMethodBuilderCore.TrackAsyncMethodCompletion ? AsyncTaskMethodBuilder<TResult>.CreateDebugFinalizableAsyncStateMachineBox<TStateMachine>() : new AsyncTaskMethodBuilder<TResult>.AsyncStateMachineBox<TStateMachine>()); taskField = asyncStateMachineBox3; asyncStateMachineBox3.StateMachine = stateMachine; asyncStateMachineBox3.Context = executionContext; if (TplEventSource.Log.IsEnabled()) { TplEventSource.Log.TraceOperationBegin(asyncStateMachineBox3.Id, "Async: " + stateMachine.GetType().Name, 0L); } if (System.Threading.Tasks.Task.s_asyncDebuggingEnabled) { System.Threading.Tasks.Task.AddToActiveTasks(asyncStateMachineBox3); } return asyncStateMachineBox3; }
看到了 ExecutionContext 类,一个很特殊的类,用于盛放其他上下文的容器,这里的作用捕捉当前线程的上下文,当线程切换的时候为线程提供运行环境,方便以后继续调用。
继续看AwaitUnsafeOnCompleted 方法:
AwaitUnsafeOnCompleted
internal static void AwaitUnsafeOnCompleted<TAwaiter>(ref TAwaiter awaiter, IAsyncStateMachineBox box) where TAwaiter : ICriticalNotifyCompletion { //走以下逻辑 if (default(TAwaiter) != null && awaiter is ITaskAwaiter) { ref TaskAwaiter ptr = ref Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter); //走此逻辑 TaskAwaiter.UnsafeOnCompletedInternal(ptr.m_task, box, true); return; } //以下代码不走被我删除 ....... } //往下走 internal static void UnsafeOnCompletedInternal(Task task, IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext) { if (TplEventSource.Log.IsEnabled() || Task.s_asyncDebuggingEnabled) { task.SetContinuationForAwait(TaskAwaiter.OutputWaitEtwEvents(task, stateMachineBox.MoveNextAction), continueOnCapturedContext, false); return; } //走此逻辑 task.UnsafeSetContinuationForAwait(stateMachineBox, continueOnCapturedContext); } //往下走 internal void UnsafeSetContinuationForAwait(IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext) { //true if (continueOnCapturedContext) { //synchronizationContext=null 跳过 SynchronizationContext synchronizationContext = SynchronizationContext.Current; if (synchronizationContext != null && synchronizationContext.GetType() != typeof(SynchronizationContext)) { SynchronizationContextAwaitTaskContinuation synchronizationContextAwaitTaskContinuation = new SynchronizationContextAwaitTaskContinuation(synchronizationContext, stateMachineBox.MoveNextAction, false); if (!this.AddTaskContinuation(synchronizationContextAwaitTaskContinuation, false)) { synchronizationContextAwaitTaskContinuation.Run(this, false); } return; } // internalCurrent =null 跳过 TaskScheduler internalCurrent = TaskScheduler.InternalCurrent; if (internalCurrent != null && internalCurrent != TaskScheduler.Default) { TaskSchedulerAwaitTaskContinuation taskSchedulerAwaitTaskContinuation = new TaskSchedulerAwaitTaskContinuation(internalCurrent, stateMachineBox.MoveNextAction, false); if (!this.AddTaskContinuation(taskSchedulerAwaitTaskContinuation, false)) { taskSchedulerAwaitTaskContinuation.Run(this, false); } return; } } //查看异步任务是否完成,完成返回true,直接将后续任务交给线程池处理,否则注册回调任务 if (!this.AddTaskContinuation(stateMachineBox, false)) { ThreadPool.UnsafeQueueUserWorkItemInternal(stateMachineBox, true); } } //往下走 private bool AddTaskContinuation(object tc, bool addBeforeOthers) { return !this.IsCompleted && ((this.m_continuationObject == null && Interlocked.CompareExchange(ref this.m_continuationObject, tc, null) == null) || this.AddTaskContinuationComplex(tc, addBeforeOthers)); } //最后将任务注册进入 m_continuationObject,这是存储回调的字段,task完成后会执行。 Interlocked.CompareExchange(ref this.m_continuationObject, tc, null)
m_continuationObject 什么时候调用呢 ,task完成的时候会调用。
task相关
internal void FinishContinuations() { object obj = Interlocked.Exchange(ref this.m_continuationObject, Task.s_taskCompletionSentinel); if (obj != null) { this.RunContinuations(obj); } }
task 完成后 会调用 回调。
所以整个流程走了一遍基本清晰了,明白了await 和async背后的东西,以后我们在使用的时候也会更加得心应手。
总结
个人理解 await 和 async 背后实际上是状态机,将await 后面的代码注册为了 task完成后的回调任务,这样保证了先后顺序。其实写这篇文章前 最大的疑惑是 为什么注册的回调能在task之后运行,没看到相应的逻辑代码,只是凭借自己的理解 代码可能会走 ThreadPool.UnsafeQueueUserWorkItemInternal(stateMachineBox, true); 这部分代码,就是把回调丢到线程池里去完成,这就让我很懵,这怎么能保证先后顺序呢,于是自己调试走了一遍流程才发现 原来不走这个逻辑,即使走这个逻辑, 那么task任务也是完成了的,始终保持逻辑的先后顺序。所以还是需要实践,想当然是行不通的。
附:不妨看看下面的文章
https://stackoverflow.com/questions/55811416/why-does-continuation-start-before-the-getresult
ExecutionContext 综述
task await async 解析
走进task 任务回调与执行