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.builderstart方法
5.返回machine.builder.Task
image.png

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);
}

整个例子实现的流程大致如下
image.png
最终总结
在async和await生成的task运行时,会生成一个异步状态机,在这个异步状态机内部会生成下个阶段要执行task的Taskawaiter对象,然后抓取当前task的上下文,和当前的异步状态机存入到IAsyncStateMachineBox中,再将该machineBox保存到Taskawaiter的m_continuationObject中。在下个阶段task执行完后,回调m_continuationObject里边异步状态机的MoveNext,层层往上回调。

posted @ 2021-06-26 15:56  路漫且远兮  阅读(998)  评论(0编辑  收藏  举报