# UGUI的InputField(輸入框)組件的介紹及使用 ## 1. 什麼是UGUI的InputField組件? UGUI的InputField組件是Unity中的一個用戶界面組件,用於接收用戶的輸入。它可以用於創建文本輸入框、密碼輸入框等功能。 ## 2. UGUI的InputField組件的 ...
async/await 源碼解析
這篇文章主要是分析 async/await 這個語法糖,分析一下 async 和 await 是如何做到非同步的。首先,我先拋出兩個問題,各位可以先想一下。
- await 之後的方法是何時執行,如何執行的?
- 為什麼 await 之後的代碼會在不同的線程執行?
demo
要想知道 async/await 是怎麼運行的,需要先寫一個demo,然後進行一下反編譯,就可以得到 async/await 編譯後的代碼,然後就可以開始分析了。
下麵是簡單使用 async/await 的demo:
static async Task Main(string[] args)
{
Console.WriteLine("1"+Thread.CurrentThread.ManagedThreadId.ToString());
await GetThreadID2();
Console.WriteLine("2"+Thread.CurrentThread.ManagedThreadId.ToString());
}
public async static Task GetThreadID2()
{
Console.WriteLine("3"+Thread.CurrentThread.ManagedThreadId.ToString());
await Task.Run(() => Console.WriteLine("4"+Thread.CurrentThread.ManagedThreadId.ToString()));
Console.WriteLine("5"+Thread.CurrentThread.ManagedThreadId.ToString());
}
然後我們來反編譯一下這段代碼(可以使用 dnSpy 進行反編譯):
namespace AsyncTest
{
internal class Program
{
[DebuggerStepThrough]
private static Task Main(string[] args)
{
Program.<Main>d__0 <Main>d__ = new Program.<Main>d__0();
<Main>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
<Main>d__.args = args;
<Main>d__.<>1__state = -1;
<Main>d__.<>t__builder.Start<Program.<Main>d__0>(ref <Main>d__);
return <Main>d__.<>t__builder.Task;
}
[DebuggerStepThrough]
public static Task GetThreadID2()
{
Program.<GetThreadID2>d__2 <GetThreadID2>d__ = new Program.<GetThreadID2>d__2();
<GetThreadID2>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
<GetThreadID2>d__.<>1__state = -1;
<GetThreadID2>d__.<>t__builder.Start<Program.<GetThreadID2>d__2>(ref <GetThreadID2>d__);
return <GetThreadID2>d__.<>t__builder.Task;
}
[DebuggerStepThrough]
private static void <Main>(string[] args)
{
Program.Main(args).GetAwaiter().GetResult();
}
[CompilerGenerated]
private sealed class <Main>d__0 : IAsyncStateMachine
{
void IAsyncStateMachine.MoveNext()
{
int num = this.<>1__state;
try
{
TaskAwaiter awaiter;
if (num != 0)
{
Console.WriteLine("1" + Thread.CurrentThread.ManagedThreadId.ToString());
awaiter = Program.GetThreadID2().GetAwaiter();
if (!awaiter.IsCompleted)
{
this.<>1__state = 0;
this.<>u__1 = awaiter;
Program.<Main>d__0 <Main>d__ = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter, ref <Main>d__);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter);
this.<>1__state = -1;
}
awaiter.GetResult();
Console.WriteLine("2" + Thread.CurrentThread.ManagedThreadId.ToString());
Console.ReadLine();
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}
}
[CompilerGenerated]
[Serializable]
private sealed class <>c
{
internal void <GetThreadID2>b__2_0()
{
Console.WriteLine("4" + Thread.CurrentThread.ManagedThreadId.ToString());
}
public static readonly Program.<>c <>9 = new Program.<>c();
public static Func<string> <>9__1_0;
public static Action <>9__2_0;
}
[CompilerGenerated]
private sealed class <GetThreadID2>d__2 : IAsyncStateMachine
{
void IAsyncStateMachine.MoveNext()
{
int num = this.<>1__state;
try
{
TaskAwaiter awaiter;
if (num != 0)
{
Console.WriteLine("3" + Thread.CurrentThread.ManagedThreadId.ToString());
awaiter = Task.Run(new Action(Program.<>c.<>9.<GetThreadID2>b__2_0)).GetAwaiter();
if (!awaiter.IsCompleted)
{
this.<>1__state = 0;
this.<>u__1 = awaiter;
Program.<GetThreadID2>d__2 <GetThreadID2>d__ = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<GetThreadID2>d__2>(ref awaiter, ref <GetThreadID2>d__);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter);
this.<>1__state = -1;
}
awaiter.GetResult();
Console.WriteLine("5" + Thread.CurrentThread.ManagedThreadId.ToString());
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine){}
public int <>1__state;
public AsyncTaskMethodBuilder <>t__builder;
private TaskAwaiter <>u__1;
}
}
}
上面的代碼是反編譯出來的所有代碼,如果各位選擇太長不看的話,這裡還有個簡化版本:
private static Task Main(string[] args)
<Main>d__.<>t__builder.Start<Program.<Main>d__0>(ref <Main>d__);
<Main>d__0.MoveNext()
<GetThreadID2>d__.<>t__builder.Start<Program.<GetThreadID2>d__2>(ref <GetThreadID2>d__);
<GetThreadID2>d__2.MoveNext()
<GetThreadID2>d__2.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<GetThreadID2>d__2>(ref awaiter, ref <GetThreadID2>d__);
<GetThreadID2>d__2.MoveNext()
<Main>d__0.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter, ref <Main>d__);
<Main>d__0.MoveNext()
入口
我們先來看一下 Main 方法。
private static Task Main(string[] args)
{
Program.<Main>d__0 <Main>d__ = new Program.<Main>d__0();
<Main>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
<Main>d__.args = args;
<Main>d__.<>1__state = -1;
<Main>d__.<>t__builder.Start<Program.<Main>d__0>(ref <Main>d__);
return <Main>d__.<>t__builder.Task;
}
首先第一行,構建了一個 Program.<Main>d__0
類型實例,嗯? 這個類哪裡來的,沒寫過呀。
哎嘿,這個類就是編譯器幫我們實現的。每一個 async/await 方法,編譯器都會幫我們實現一個這樣的類。
簡單分析一下這個方法,實例化一個 Program.<Main>d__0()
, 初始化 1__state
值為-1,調用 Program.<Main>d__0()
的 Start
方法。
Start
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);
RuntimeHelpers.PrepareConstrainedRegions();
try
{
ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher);
stateMachine.MoveNext();
}
finally
{
executionContextSwitcher.Undo();
}
}
Start 代碼的主要作用就是啟動 stateMachine,即 stateMachine.MoveNext()
。
MoveNext
MoveNext 是 async/await 里非常重要的一段代碼,這個方法控制了整個非同步任務的執行步驟。
我們可以將其分解成三個部分,即
- await 之前
- await 執行
- await 之後的代碼
具體的分析各位請看註釋。
void IAsyncStateMachine.MoveNext()
{
// 這裡 <>1__state 之前初始化的時候是 -1,所以 num 就是-1
int num = this.<>1__state;
try
{
TaskAwaiter awaiter;
// 第一次進來,num 是 -1 所以接下來的邏輯
if (num != 0)
{
// 這裡執行 await 之前的代碼
Console.WriteLine("1" + Thread.CurrentThread.ManagedThreadId.ToString());
// 這裡就是執行 await 方法
awaiter = Program.GetThreadID2().GetAwaiter();
// 判斷是否執行完成,通常第一次進來 IsCompleted 都是 false
if (!awaiter.IsCompleted)
{
// 修改狀態,下次再執行 MoveNext 就不會繼續走這段邏輯來了
this.<>1__state = 0;
this.<>u__1 = awaiter;
Program.<Main>d__0 <Main>d__ = this;
// 這裡其實是往 Task 里添加了一個 任務結束時的回調,在任務結束時會再次調用 MoveNext
// 這就解釋了為什麼 await 之後的方法是另外一個線程,因為await 之後的方法是在 下一個 MoveNext 的里調用的
// 而那個 MoveNext 是由線程池挑選一個線程進行執行的
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter, ref <Main>d__);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter);
this.<>1__state = -1;
}
// GetResult 里有一個 死迴圈 等待結果
awaiter.GetResult();
// await 之後的方法
Console.WriteLine("2" + Thread.CurrentThread.ManagedThreadId.ToString());
Console.ReadLine();
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}
await 之前的代碼
// 這裡執行 await 之前的代碼
Console.WriteLine("1" + Thread.CurrentThread.ManagedThreadId.ToString());
await 之前的代碼很簡單,就是正常依次執行。
await 的代碼
await 的代碼大都以 Task 形式執行,主要分為兩步。
第一步,將 Task 推入線程池中。
awaiter = Task.Run(new Action(Program.<>c.<>9.<GetThreadID2>b__3_0)).GetAwaiter();
public static Task Run(Action action)
{
StackCrawlMark stackCrawlMark = StackCrawlMark.LookForMyCaller;
return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default, TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackCrawlMark);
}
internal static Task InternalStartNew(Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler, TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)
{
Task task = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
task.ScheduleAndStart(false);
return task;
}
internal void ScheduleAndStart(bool needsProtection)
{
try
{
this.m_taskScheduler.InternalQueueTask(this);
}
catch (ThreadAbortException exceptionObject){}
}
internal void InternalQueueTask(Task task)
{
this.QueueTask(task);
}
第二步,線程池執行 Task。
void IThreadPoolWorkItem.ExecuteWorkItem()
{
this.ExecuteEntry(false);
}
internal bool ExecuteEntry(bool bPreventDoubleExecution)
{
if (!this.IsCancellationRequested && !this.IsCanceled)
{
this.ExecuteWithThreadLocal(ref Task.t_currentTask);
}
return true;
}
private void ExecuteWithThreadLocal(ref Task currentTaskSlot)
{
Task task = currentTaskSlot;
TplEtwProvider log = TplEtwProvider.Log;
Guid currentThreadActivityId = default(Guid);
bool flag = log.IsEnabled();
try
{
currentTaskSlot = this;
ExecutionContext capturedContext = this.CapturedContext;
if (capturedContext == null)
{
this.Execute();
}
else
{
if (this.IsSelfReplicatingRoot || this.IsChildReplica)
{
this.CapturedContext = Task.CopyExecutionContext(capturedContext);
}
ContextCallback contextCallback = Task.s_ecCallback;
if (contextCallback == null)
{
contextCallback = (Task.s_ecCallback = new ContextCallback(Task.ExecutionContextCallback));
}
// 這裡是執行 Task 方法的地方
ExecutionContext.Run(capturedContext, contextCallback, this, true);
}
this.Finish(true);
}
finally
{
currentTaskSlot = task;
}
}
await 之後的代碼
那麼 await 之後的代碼是如何執行的呢?
大家回頭看一下 MoveNext
方法,可以看到首次執行 MoveNext
的時候,代碼的邏輯是這樣的
if(num!=0) -> if(!awaiter.IsCompleted) -> AwaitUnsafeOnCompleted;return;
關鍵就在於 AwaitUnsafeOnCompleted
這個方法,來看一下 AwaitUnsafeOnCompleted
方法。
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
{
try
{
AsyncMethodBuilderCore.MoveNextRunner runner = null;
// 這裡是準備一個 任務執行完的回調,簡單理解成是將 MoveNext 包裝成一個 action
Action completionAction = this.m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runner);
if (this.m_coreState.m_stateMachine == null)
{
Task<TResult> task = this.Task;
this.m_coreState.PostBoxInitialization(stateMachine, runner, task);
}
// 將 completionAction 推到 task里,具體看一下接下來的代碼
awaiter.UnsafeOnCompleted(completionAction);
}
catch (Exception exception)
{
AsyncMethodBuilderCore.ThrowAsync(exception, null);
}
}
public void UnsafeOnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(this.m_task, continuation, true, false);
}
internal static void OnCompletedInternal(Task task, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext)
{
task.SetContinuationForAwait(continuation, continueOnCapturedContext, flowExecutionContext, ref stackCrawlMark);
}
internal void SetContinuationForAwait(Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext, ref StackCrawlMark stackMark)
{
if (!this.AddTaskContinuation(continuationAction, false))
{
AwaitTaskContinuation.UnsafeScheduleAction(continuationAction, this);
}
}
private bool AddTaskContinuation(object tc, bool addBeforeOthers)
{
//這裡就把completionAction 放到了 Task 的 m_continuationObject 里
return !this.IsCompleted && ((this.m_continuationObject == null && Interlocked.CompareExchange(ref this.m_continuationObject, tc, null) == null) || this.AddTaskContinuationComplex(tc, addBeforeOthers));
}
至此為止,準備工作(將 MoveNext
放到 task 的回調里)已經做完了,接下來就是如何來觸發這個方法。
簡單分析一下,MoveNext
肯定是在 task 執行完觸發,讓我們回頭看看 Task 執行的代碼。會發現,哎嘿,在ExecutionContext.Run(capturedContext, contextCallback, this, true);
方法執行完後還有一句代碼 this.Finish(true)
。
internal void Finish(bool bUserDelegateExecuted)
{
this.FinishStageTwo();
}
internal void FinishStageTwo()
{
this.FinishStageThree();
}
internal void FinishStageThree()
{
this.FinishContinuations();
}
internal void FinishContinuations()
{
// 獲取之前註冊的所有回調 最簡單的就可以理解成 MoveNext
object obj = Interlocked.Exchange(ref this.m_continuationObject, Task.s_taskCompletionSentinel);
if (obj != null)
{
bool flag = (this.m_stateFlags & 134217728) == 0 && Thread.CurrentThread.ThreadState != ThreadState.AbortRequested && (this.m_stateFlags & 64) == 0;
Action action = obj as Action;
if (action != null)
{
AwaitTaskContinuation.RunOrScheduleAction(action, flag, ref Task.t_currentTask);
return;
}
}
}
internal static void RunOrScheduleAction(Action action, bool allowInlining, ref Task currentTask)
{
// 這裡調用了 MoveNextRunner.Run ,簡單理解 就是 MoveNext 方法
action();
}
void IAsyncStateMachine.MoveNext()
{
// 此時 num 就是 0 了,就會執行 await 之後的代碼了。
int num = this.<>1__state;
try
{
TaskAwaiter awaiter;
if (num != 0)
{}
else
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter);
this.<>1__state = -1;
}
awaiter.GetResult();
Console.WriteLine("2" + Thread.CurrentThread.ManagedThreadId.ToString());
Console.ReadLine();
}
catch (Exception exception)
{
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}
這一段代碼我刪掉了很多內容,但大致的執行順序就是如此,如果想要瞭解更多細節,可以查看 Task 這個類的源碼。
總結
回頭看看開頭的兩個問題,現在就可以回答了。
- await 之後的方法是何時執行,如何執行的?
await 的方法在 Task 執行完成之後,通過調用Finish
方法執行的。
具體的執行步驟是先將MoveNext
方法註冊到 Task 的回調里,然後在 Task 執行完後調用這個方法。 - 為什麼 await 之後的代碼會在不同的線程執行?
這個其實是因為 Task 的機制,Task 會被推到線程池裡,由線程池挑選一個線程去執行,await 之後的代碼其實是由這個線程去執行的,自然就跟 await 的之前的代碼不是一個線程。
PS: 補一張圖