Taskasync和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()
{await和async使用方法
}
// 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;
{
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;
}
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,层层往上回调。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论