Task async和await解析
探究学习一下task内部async和await的运行机制。本文是用dnspy进行源码探索。
用一个例子来解析具体的运行机制。首先建立一个控制台程序,在控制台程序中新增一个Test类,Test类中实现一个Say的方法,包含一个输入和一个输出。
class Program
{
static async Task Main(string[] args)
{
var result = await new Test().Say("hello");
Console.ReadKey();
}
}
public class Test
{
public async Task<string> Say(string name)
{
//await之前的代码逻辑
Console.WriteLine("before say");
await Task.Delay(1000);
//await之后的代码逻辑
Console.WriteLine("after say");
return name;
}
}
对编译好的dll文件进行反编译,发现原先代码里边的async和await已经消失了,取而代之的是一个密封类。
public class Test
{
// Token: 0x06000004 RID: 4 RVA: 0x000020C0 File Offset: 0x000002C0
[DebuggerStepThrough]
public Task<string> Say(string name)
{
Test.<Say>d__0 <Say>d__ = new Test.<Say>d__0();
<Say>d__.<>4__this = this;
<Say>d__.name = name;
<Say>d__.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
<Say>d__.<>1__state = -1;
<Say>d__.<>t__builder.Start<Test.<Say>d__0>(ref <Say>d__);
return <Say>d__.<>t__builder.Task;
}
// Token: 0x06000005 RID: 5 RVA: 0x0000210B File Offset: 0x0000030B
public Test()
{
}
// Token: 0x02000005 RID: 5
[CompilerGenerated]
private sealed class <Say>d__0 : IAsyncStateMachine
{
// Token: 0x06000009 RID: 9 RVA: 0x0000220E File Offset: 0x0000040E
public <Say>d__0()
{
}
// Token: 0x0600000A RID: 10 RVA: 0x00002218 File Offset: 0x00000418
void IAsyncStateMachine.MoveNext()
{
int num = this.<>1__state;
string result;
try
{
TaskAwaiter awaiter;
if (num != 0)
{
Console.WriteLine("before say");
awaiter = Task.Delay(1000).GetAwaiter();
if (!awaiter.IsCompleted)
{
this.<>1__state = 0;
this.<>u__1 = awaiter;
Test.<Say>d__0 <Say>d__ = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Test.<Say>d__0>(ref awaiter, ref <Say>d__);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter);
this.<>1__state = -1;
}
awaiter.GetResult();
Console.WriteLine("after say");
result = this.name;
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult(result);
}
// Token: 0x0600000B RID: 11 RVA: 0x000022F4 File Offset: 0x000004F4
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
}
// Token: 0x04000007 RID: 7
public int <>1__state;
// Token: 0x04000008 RID: 8
public AsyncTaskMethodBuilder<string> <>t__builder;
// Token: 0x04000009 RID: 9
public string name;
// Token: 0x0400000A RID: 10
public Test <>4__this;
// Token: 0x0400000B RID: 11
private TaskAwaiter <>u__1;
}
}
转变的方法大致如下:
原先的async方法会变成两部分内容,会产生一个与原来方法名一致但不带有async的方法,第二部分会产生一个密封类,根据反编译的结果来看,名字略显奇怪,此处暂定为AsyncStateMachine来指代异步状态机这个密封类,然后将原先async方法中的代码逻辑转移到异步状态机的MoveNext之中。
编译后方法执行主要做了以下几个步骤:
1.初始化一个异步状态机machine
2.初始化一个AsyncTaskMethodBuilder的实例,赋予machine.builder
3.设置异步状态机的状态为-1,将类传入到状态机内部
4.调用machine.builder的start方法
5.返回machine.builder.Task
AsyncTaskMethodBuilder.Create()
在第二步中仅仅做了个实例化操作,具体代码如下
public struct AsyncTaskMethodBuilder
{
// Token: 0x0600363D RID: 13885 RVA: 0x0015277C File Offset: 0x0015157C
public static AsyncTaskMethodBuilder Create()
{
return default(AsyncTaskMethodBuilder);
}
}
AsyncTaskMethodBuilder.Start()
在Start方法中使用的是AsyncMethodBuilderCore.Start方法,先获取当前线程的_executionContext和_synchronizationContext,再执行传入的异步状态机的MoveNext方法,即真正的逻辑代码,最后切换回开始获取的_executionContext和_synchronizationContext。
public static void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
if (stateMachine == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine);
}
Thread currentThread = Thread.CurrentThread;
Thread thread = currentThread;
ExecutionContext executionContext = currentThread._executionContext;
ExecutionContext executionContext2 = executionContext;
SynchronizationContext synchronizationContext = currentThread._synchronizationContext;
try
{
stateMachine.MoveNext();
}
finally
{
SynchronizationContext synchronizationContext2 = synchronizationContext;
Thread thread2 = thread;
if (synchronizationContext2 != thread2._synchronizationContext)
{
thread2._synchronizationContext = synchronizationContext2;
}
ExecutionContext executionContext3 = executionContext2;
ExecutionContext executionContext4 = thread2._executionContext;
if (executionContext3 != executionContext4)
{
ExecutionContext.RestoreChangedContextToThread(thread2, executionContext3, executionContext4);
}
}
}
IStateMachine.MoveNext()
再来看看stateMachine.MoveNext这个方法,选取开头例子的Say实现的异步状态机中的MoveNext方法。第一次调用首先会对状态state进行判断,执行await之前的逻辑代码,对await的Task任务创建一个TaskAwaiter对象,对于一般的Task来讲都算是延迟任务,在判断IsCompleted状态时都是处于未完成的状态,会先改变state状态,然后将这个含有task任务的awaiter方法赋给了Say异步状态机中的awaiter(注意awaiter中task指代的是上述Task.Delay(1000)这个task),再调用AwaitUnsafeOnCompleted方法后返回
//task.GetAwaiter的作用 返回一个携带task的TaskAwaiter对象
public TaskAwaiter GetAwaiter()
{
return new TaskAwaiter(this);
}
//TaskAwaiter的构造函数
internal TaskAwaiter(Task task)
{
this.m_task = task;
}
Say实现的异步状态机中的MoveNext方法具体代码如下
void IAsyncStateMachine.MoveNext()
{
int num = this.<>1__state;
string result;
try
{
TaskAwaiter awaiter;
if (num != 0)//判断状态state
{
Console.WriteLine("before say");
awaiter = Task.Delay(1000).GetAwaiter();
if (!awaiter.IsCompleted)
{
this.<>1__state = 0;
this.<>u__1 = awaiter;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Test.<Say>d__0>(ref awaiter, ref this);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter);
this.<>1__state = -1;
}
awaiter.GetResult();
Console.WriteLine("after say");
result = this.name;
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult(result);
}
AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted()
在AwaitUnsafeOnCompleted方法中,传入了awaiter以及异步状态机,正常情况下只执行了下面代码,一个是GetStateMachineBox方法,另外一个是TaskAwaiter.UnsafeOnCompletedInternal方法。在GetStateMachineBox方法中会捕获当前执行上下文executionContext,将执行上下文和异步状态机放入到stateMachineBox的Context和StateMachine进行保存。在UnsafeOnCompletedInternal方法中执行m_task.UnsafeSetContinuationForAwait方法,此时的m_task指代的是上述Task.Delay(1000)这个task
IAsyncStateMachineBox stateMachineBox = this.GetStateMachineBox<TStateMachine>(ref stateMachine);
if (default(TAwaiter) != null && awaiter is ITaskAwaiter)
{
ref TaskAwaiter ptr = ref Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter);
TaskAwaiter.UnsafeOnCompletedInternal(ptr.m_task, stateMachineBox, true);
return;
}
Task.UnsafeSetContinuationForAwait()
在UnsafeSetContinuationForAwait方法中,通过AddTaskContinuation里边的Interlocked.CompareExchange方法将包含执行上下文和异步状态机的stateMachineBox或者stateMachineBox的action方法放到task.m_continuationObject(注意这个外层的say异步状态机已存入到下一层的task中),然后执行任务。
internal void UnsafeSetContinuationForAwait(IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext)
{
if (continueOnCapturedContext)
{
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;
}
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;
}
}
if (!this.AddTaskContinuation(stateMachineBox, false))
{
ThreadPool.UnsafeQueueUserWorkItemInternal(stateMachineBox, true);
}
}
上述都是在执行task之前的代码,在执行层层task后,在最里层的task执行完毕后会执行一个Task.TrySetResult方法。这个方法会调用FinishContinuations方法,FinishContinuations方法会将task.m_continuationObject(say异步状态机)重新取出来,放入到RunContinuations中,使用AwaitTaskContinuation.RunOrScheduleAction方法调用这个上层的异步状态机的MoveNext方法。
internal bool TrySetResult()
{
if (this.AtomicStateUpdate(83886080, 90177536))
{
Task.ContingentProperties contingentProperties = this.m_contingentProperties;
if (contingentProperties != null)
{
this.NotifyParentIfPotentiallyAttachedTask();
contingentProperties.SetCompleted();
}
this.FinishContinuations();
return true;
}
return false;
}
internal void FinishContinuations()
{
object obj = Interlocked.Exchange(ref this.m_continuationObject, Task.s_taskCompletionSentinel);
if (obj != null)
{
this.RunContinuations(obj);
}
}
//RunContinuations部分代码
private void RunContinuations(object continuationObject)
{
TplEventSource tplEventSource = TplEventSource.Log;
if (!tplEventSource.IsEnabled())
{
tplEventSource = null;
}
if (AsyncCausalityTracer.LoggingOn)
{
AsyncCausalityTracer.TraceSynchronousWorkStart(this, CausalitySynchronousWork.CompletionNotification);
}
bool flag = (this.m_stateFlags & 64) == 0 && RuntimeHelpers.TryEnsureSufficientExecutionStack();
IAsyncStateMachineBox asyncStateMachineBox = continuationObject as IAsyncStateMachineBox;
if (asyncStateMachineBox != null)
{
AwaitTaskContinuation.RunOrScheduleAction(asyncStateMachineBox, flag);
Task.LogFinishCompletionNotification();
return;
}
Action action = continuationObject as Action;
if (action != null)
{
AwaitTaskContinuation.RunOrScheduleAction(action, flag);
Task.LogFinishCompletionNotification();
return;
}
//省略部分....
}
回过头来再看看Say实现的异步状态机中的MoveNext的代码。在第二次执行MoveNext时,执行await后半部分逻辑代码,设置状态码,然后执行SetResult方法,这个方法内部会调用上面的Task.TrySetResult方法,从而接着回调用上层异步状态机的MoveNext方法。
void IAsyncStateMachine.MoveNext()
{
int num = this.<>1__state;
string result;
try
{
TaskAwaiter awaiter;
if (num != 0)//判断状态state
{
Console.WriteLine("before say");
awaiter = Task.Delay(1000).GetAwaiter();
if (!awaiter.IsCompleted)
{
this.<>1__state = 0;
this.<>u__1 = awaiter;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Test.<Say>d__0>(ref awaiter, ref this);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter);
this.<>1__state = -1;
}
awaiter.GetResult();
Console.WriteLine("after say");
result = this.name;
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult(result);
}
整个例子实现的流程大致如下
最终总结
在async和await生成的task运行时,会生成一个异步状态机,在这个异步状态机内部会生成下个阶段要执行task的Taskawaiter对象,然后抓取当前task的上下文,和当前的异步状态机存入到IAsyncStateMachineBox中,再将该machineBox保存到Taskawaiter的m_continuationObject中。在下个阶段task执行完后,回调m_continuationObject里边异步状态机的MoveNext,层层往上回调。