C#異步原理詳情
前言:
用async關(guān)鍵字和await表達式表達的異步操作在C#5便發(fā)布了,其使用簡單,讓開發(fā)者能夠用同步的方式來書寫異步代碼,真的很棒。當然,編譯器在后面也做了不少工作--一個異步方法本質(zhì)上是被編譯器轉(zhuǎn)換為一個樁方法和一個狀態(tài)機。隨著C#版本的不斷更新,可能編譯器轉(zhuǎn)換后的代碼有所變化,但本質(zhì)的東西應(yīng)該不會變太多。這篇筆記來源于C# in depth(第四版),記錄一些關(guān)鍵的地方,便于自己記憶,希望對你也有所幫助。

文章結(jié)論主要以下五點:
- (1)使用
builder作為異步基礎(chǔ)架構(gòu),async方法會被轉(zhuǎn)換成樁方法和狀態(tài)機。 - (2)狀態(tài)機會追蹤
builder、方法參數(shù)、局部變量、awaiter以及續(xù)延中需要恢復(fù)執(zhí)行的位置。 - (3)編譯器會創(chuàng)建一些代碼,旨在在方法恢復(fù)時回到方法內(nèi)部。
- (4)
INotifyCompletion和ICriticalNotifyCompletion接口可用于控制執(zhí)行上下文的貫穿。 - (5)
builder方法由編譯器負責調(diào)用。
下面我們就上面的5點展開說明。
一、關(guān)于第一點的說明
我們編寫的async方法會被編譯器編譯為一個樁方法和一個狀態(tài)機。
樁方法的簽名和async方法的簽名一致。在樁方法的內(nèi)部,會首先new一個狀態(tài)機,然后初始化這個狀態(tài)機,初始化的過程主要包含
①狀態(tài)機的狀態(tài)字段(用于記錄await表達式完成后的恢復(fù)執(zhí)行處)、
②builder(AsyncTaskMethodBuilder)的初始化、
③捕獲async方法參數(shù)后提升為字段的值。狀態(tài)機初始化好之后,就要調(diào)用狀態(tài)機的builder字段(一般情況下是AsyncTaskMethodBuilder)的Start方法,之后返回builder字段的Task屬性。在這個過程中要注意狀態(tài)機和builder都是一個值類型,所以在builder上執(zhí)行的Start參數(shù)有ref修飾符,表示按引用傳遞,原因是在await方法回調(diào)后值類型的狀態(tài)能夠保存。
至于async方法中的執(zhí)行邏輯,則全部被轉(zhuǎn)移到了狀態(tài)機的MoveNext方法中。
樁方法和狀態(tài)機之間的聯(lián)系就是通過builder來建立的。
二、關(guān)于第二點的說明
狀態(tài)機會保存builder的一個字段,async方法的參數(shù)也會被提升為狀態(tài)機的字段,如果在async方法的await表達式之后需要訪問async方法中的局部變量,也需要將該局部變量保存到狀態(tài)機中。狀態(tài)機還保存了awaiter字段,一般情況下,不同類型的await表達式只保存一個就行。至于await之后續(xù)延中需要恢復(fù)執(zhí)行的位置由一個state的字段來表示,當state字段的值為-1時表示為執(zhí)行或正在執(zhí)行,大于0時表示暫定,-2表示已經(jīng)結(jié)束,結(jié)束表示正常完成或有異常。
三、關(guān)于第三點的說明
狀態(tài)機中創(chuàng)建了大量的樣板代碼來“翻譯”async方法中的代碼,一般我們通過反編譯工具可以看到大量的switch、goto之類的語句。
//MoveNext方法的樣板代碼
void IAsyncStateMachine. MoveNext() {
try
{
switch (this. state)
{
default: goto MethodStart;
case 0: goto Label0A;
case 1: goto Label1A;
case 2: goto Label2A; <------ case 的數(shù)量與await表達式數(shù)量相等
}
MethodStart: <------ 第一個await表達式之前的代碼
<------ 設(shè)置第一個awaiter
Label0A: <------ 從續(xù)延中恢復(fù)執(zhí)行的代碼
Label0B: <------ 快速路徑和慢速路徑匯合之處
<------ 剩余代碼,包括更多標簽以及awaiter等
}
catch (Exception e) //(本行及以下5行) 通過builder填充所有異常信息
{
this.state = -2;
builder.SetException(e);
return;
}
this.state = -2; //(本行及以下1行)通過builder填充方法完成的信息
builder.SetResult();
}
四、關(guān)于第四點的說明
ICriticalNotifyCompletion可以在基礎(chǔ)框架配合下實現(xiàn)執(zhí)行上下文(回調(diào))的安全訪問,INotifyCompletion需要通過ExecutionContext類來配合完成(ExecutionContext的Capture和Run)。這兩個接口對應(yīng)builder的AwaitOnCompleted<TAwaiter,TStateMachine>(ref TAwaiterawaiter,ref TStateMachinestateMachine)方法和AwaitUnsafeOnCompleted<TAwaiter,TStateMachine>(ref TAwaiterawaiter,ref TStateMachinestateMachine)方法,也會被后者進行調(diào)用。(builder具體要使用哪一個方法要看Task實現(xiàn)了哪一個接口)
五、關(guān)于第五點的說明
builder和狀態(tài)機等等都是編譯器生成的,至于調(diào)用么,當然得由編譯器負責調(diào)用了。
到此這篇關(guān)于C#異步原理詳情的文章就介紹到這了,更多相關(guān)C#異步原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c#創(chuàng)建vc可調(diào)用的com組件方法分享
本文詳細闡述如何用C#創(chuàng)建一個COM組件,并能用VC6.0等調(diào)用,大家參考使用2013-12-12

