C#異步原理詳情
前言:
用async
關(guān)鍵字和await
表達(dá)式表達(dá)的異步操作在C#5便發(fā)布了,其使用簡(jiǎn)單,讓開(kāi)發(fā)者能夠用同步的方式來(lái)書(shū)寫(xiě)異步代碼,真的很棒。當(dāng)然,編譯器在后面也做了不少工作--一個(gè)異步方法本質(zhì)上是被編譯器轉(zhuǎn)換為一個(gè)樁方法和一個(gè)狀態(tài)機(jī)。隨著C#版本的不斷更新,可能編譯器轉(zhuǎn)換后的代碼有所變化,但本質(zhì)的東西應(yīng)該不會(huì)變太多。這篇筆記來(lái)源于C# in depth(第四版),記錄一些關(guān)鍵的地方,便于自己記憶,希望對(duì)你也有所幫助。
文章結(jié)論主要以下五點(diǎn):
- (1)使用
builder
作為異步基礎(chǔ)架構(gòu),async
方法會(huì)被轉(zhuǎn)換成樁方法和狀態(tài)機(jī)。 - (2)狀態(tài)機(jī)會(huì)追蹤
builder
、方法參數(shù)、局部變量、awaiter
以及續(xù)延中需要恢復(fù)執(zhí)行的位置。 - (3)編譯器會(huì)創(chuàng)建一些代碼,旨在在方法恢復(fù)時(shí)回到方法內(nèi)部。
- (4)
INotifyCompletion和ICriticalNotifyCompletion
接口可用于控制執(zhí)行上下文的貫穿。 - (5)
builder
方法由編譯器負(fù)責(zé)調(diào)用。
下面我們就上面的5點(diǎn)展開(kāi)說(shuō)明。
一、關(guān)于第一點(diǎn)的說(shuō)明
我們編寫(xiě)的async
方法會(huì)被編譯器編譯為一個(gè)樁方法和一個(gè)狀態(tài)機(jī)。
樁方法的簽名和async
方法的簽名一致。在樁方法的內(nèi)部,會(huì)首先new
一個(gè)狀態(tài)機(jī),然后初始化這個(gè)狀態(tài)機(jī),初始化的過(guò)程主要包含
①狀態(tài)機(jī)的狀態(tài)字段(用于記錄await表達(dá)式完成后的恢復(fù)執(zhí)行處)、
②builder
(AsyncTaskMethodBuilder
)的初始化、
③捕獲async
方法參數(shù)后提升為字段的值。狀態(tài)機(jī)初始化好之后,就要調(diào)用狀態(tài)機(jī)的builder
字段(一般情況下是AsyncTaskMethodBuilder
)的Start
方法,之后返回builder
字段的Task
屬性。在這個(gè)過(guò)程中要注意狀態(tài)機(jī)和builder都是一個(gè)值類(lèi)型,所以在builder上執(zhí)行的Start
參數(shù)有ref修飾符,表示按引用傳遞,原因是在await
方法回調(diào)后值類(lèi)型的狀態(tài)能夠保存。
至于async
方法中的執(zhí)行邏輯,則全部被轉(zhuǎn)移到了狀態(tài)機(jī)的MoveNext
方法中。
樁方法和狀態(tài)機(jī)之間的聯(lián)系就是通過(guò)builder
來(lái)建立的。
二、關(guān)于第二點(diǎn)的說(shuō)明
狀態(tài)機(jī)會(huì)保存builder
的一個(gè)字段,async
方法的參數(shù)也會(huì)被提升為狀態(tài)機(jī)的字段,如果在async
方法的await
表達(dá)式之后需要訪問(wèn)async
方法中的局部變量,也需要將該局部變量保存到狀態(tài)機(jī)中。狀態(tài)機(jī)還保存了awaiter字段,一般情況下,不同類(lèi)型的await表達(dá)式只保存一個(gè)就行。至于await之后續(xù)延中需要恢復(fù)執(zhí)行的位置由一個(gè)state
的字段來(lái)表示,當(dāng)state字段的值為-1時(shí)表示為執(zhí)行或正在執(zhí)行,大于0時(shí)表示暫定,-2表示已經(jīng)結(jié)束,結(jié)束表示正常完成或有異常。
三、關(guān)于第三點(diǎn)的說(shuō)明
狀態(tài)機(jī)中創(chuàng)建了大量的樣板代碼來(lái)“翻譯”async
方法中的代碼,一般我們通過(guò)反編譯工具可以看到大量的switch
、goto
之類(lèi)的語(yǔ)句。
//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表達(dá)式數(shù)量相等 } MethodStart: <------ 第一個(gè)await表達(dá)式之前的代碼 <------ 設(shè)置第一個(gè)awaiter Label0A: <------ 從續(xù)延中恢復(fù)執(zhí)行的代碼 Label0B: <------ 快速路徑和慢速路徑匯合之處 <------ 剩余代碼,包括更多標(biāo)簽以及awaiter等 } catch (Exception e) //(本行及以下5行) 通過(guò)builder填充所有異常信息 { this.state = -2; builder.SetException(e); return; } this.state = -2; //(本行及以下1行)通過(guò)builder填充方法完成的信息 builder.SetResult(); }
四、關(guān)于第四點(diǎn)的說(shuō)明
ICriticalNotifyCompletion
可以在基礎(chǔ)框架配合下實(shí)現(xiàn)執(zhí)行上下文(回調(diào))的安全訪問(wèn),INotifyCompletion
需要通過(guò)ExecutionContext
類(lèi)來(lái)配合完成(ExecutionContext的Capture和Run)。這兩個(gè)接口對(duì)應(yīng)builder
的AwaitOnCompleted<TAwaiter,TStateMachine>(ref TAwaiterawaiter,ref TStateMachinestateMachine)方法和AwaitUnsafeOnCompleted<TAwaiter,TStateMachine>(ref TAwaiterawaiter,ref TStateMachinestateMachine)方法,也會(huì)被后者進(jìn)行調(diào)用。(builder
具體要使用哪一個(gè)方法要看Task實(shí)現(xiàn)了哪一個(gè)接口)
五、關(guān)于第五點(diǎn)的說(shuō)明
builder
和狀態(tài)機(jī)等等都是編譯器生成的,至于調(diào)用么,當(dāng)然得由編譯器負(fù)責(zé)調(diào)用了。
到此這篇關(guān)于C#異步原理詳情的文章就介紹到這了,更多相關(guān)C#異步原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c#創(chuàng)建vc可調(diào)用的com組件方法分享
本文詳細(xì)闡述如何用C#創(chuàng)建一個(gè)COM組件,并能用VC6.0等調(diào)用,大家參考使用2013-12-12c# RSA非對(duì)稱(chēng)加解密及XML&PEM格式互換方案
這篇文章主要介紹了c# RSA非對(duì)稱(chēng)加解密及XML&PEM格式互換方案,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下2020-12-12C#代碼實(shí)現(xiàn)短信驗(yàn)證碼接口示例
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)短信驗(yàn)證碼接口示例代碼,感興趣的小伙伴們可以參考一下2016-08-08C#解析char型指針?biāo)赶虻膬?nèi)容(實(shí)例解析)
在c++代碼中定義了一個(gè)功能函數(shù),這個(gè)功能函數(shù)會(huì)將計(jì)算的結(jié)果寫(xiě)入一個(gè)字符串型的數(shù)組中output,然后c#會(huì)調(diào)用c++導(dǎo)出的dll中的接口函數(shù),然后獲取這個(gè)output并解析成string類(lèi)型,本文通過(guò)實(shí)例解析C#?char型指針?biāo)赶虻膬?nèi)容,感興趣的朋友一起看看吧2024-03-03