1. 程式人生 > 其它 >Task async和await解析

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,層層往上回調。