在 .NET 4.0 以后,基于 Task 的異步編程模式大行其道,因其大大簡(jiǎn)化了異步編程所帶來(lái)的大量代碼工作而深受編程人員的歡迎,如果你曾經(jīng)使用過(guò) APM(基于 IAsyncResult) 和 EAP( 基于 event/delegate),那么你一定感受頗深。
而隨之而來(lái).NET 4.5 的兩個(gè)關(guān)鍵字 async 和 await 又使得異步編程如編寫(xiě)順序的代碼一樣容易,特別是 async 對(duì) 委托(Lamda/LINQ 表達(dá)式,匿名委托)的支持,使得async 和 await 成為異步編程的代名詞。
但是我們都知道,異步編程的背后是多線程的技術(shù),線程處理,線程間的通訊,線程的管理一直是編程世界里比較難于掌握的部分,那么 async 和 await 關(guān)鍵字究竟有什么魔法能夠把復(fù)雜的線程處理變成簡(jiǎn)單的兩個(gè)關(guān)鍵字而已那?
我相信對(duì) .NET 框架有過(guò)了解的人都知道,微軟喜歡把底層的東西大肆的進(jìn)行封裝,力求把用戶(hù)當(dāng)成傻子看(也不是不好,用戶(hù)其實(shí)不需要知道產(chǎn)品細(xì)節(jié)),這樣使得C#/.Net 容易學(xué)習(xí),但是也使得程序員知其然而不知其所以然。
那么我們?cè)趺磳W(xué)習(xí) .NET 光亮功能背后的關(guān)鍵字的技術(shù)及其實(shí)現(xiàn)原理那?讀文章,但是還有一種方式,看源代碼(MSIL),使用反編譯工具如(Reflector) 查看編譯的代碼以了解背后的運(yùn)行機(jī)制,使用這種方式我們知道了 using, lock, event, delegate 的背后機(jī)制,我們依樣畫(huà)葫蘆來(lái)解析一下 async 與 await 關(guān)鍵字。
在開(kāi)始之前,我們先看一段 C# 代碼:
static void Main(string[] args) { CountAsync(); Console.WriteLine("Async run back to main"); Console.Read(); } PRivate static async void CountAsync() { Console.WriteLine("Async run "); await Task.Run(() => { for (int i = 0; i < 10; i++) { Console.WriteLine(i); Thread.Sleep(100); } } ); Console.WriteLine("Async completed"); }然后看一下他的輸出, async 與 await 工作的如同 MSDN 所說(shuō)的一致(廢話01): -- 當(dāng)方法執(zhí)行到 await 時(shí),控制權(quán)返回調(diào)用方,然后等待方法執(zhí)行完成獲取控制權(quán),然后執(zhí)行 await 后的代碼。
那么現(xiàn)在我們用反編譯器這個(gè)照妖鏡來(lái)照照這一個(gè) async 和 await 究竟是何方妖魔 : ( 注意要勾選顯示編譯代碼):
神馬?怎么多出來(lái)這么多不知所謂的東西,我明明只編寫(xiě)了兩個(gè)方法 Main 和 CountAsync, 其它的是神馬東東。 微軟,不編譯器你究竟對(duì)我的代碼做了什么,讓她(它)變成這樣了?
好吧,讓我們逐條看一下:
基本上和我們寫(xiě)的一樣。沒(méi)有什么特別,沒(méi)有什么可疑,當(dāng)然就沒(méi)有什么好說(shuō)的!
private static void Main(string[] args) { Program.CountAsync(); Console.WriteLine("Async run back to main"); Console.Read(); }這個(gè)不是我寫(xiě)的CountAsync 方法么?
我的 async 關(guān)鍵字,我的await Task.Run 都去那兒了?
為了揭開(kāi)這個(gè)謎底,找出真相,真相永遠(yuǎn)只有一個(gè),我們先看一下方法的構(gòu)成:
[DebuggerStepThrough] [AsyncStateMachine(typeof (Program.<CountAsync>d__2))] private static void CountAsync() { Program.<CountAsync>d__2 stateMachine; stateMachine.t__builder = AsyncVoidMethodBuilder.Create(); stateMachine.1__state = -1; stateMachine.t__builder.Start<Program.<CountAsync>d__2>(ref stateMachine); }private static Action $__CachedAnonymousMethodDelegate1;
生成了一個(gè) Action 委托,巧合的是,我們?cè)谡{(diào)用 Task.Run 方法啟動(dòng)一個(gè)新的 Task 時(shí),傳的也是一個(gè)Action 委托,難道這是一個(gè)巧合,還是別有用意?
一個(gè)方法,代碼示例如下,沒(méi)啥特別,是個(gè)程序猿就能寫(xiě),但是它是怎么來(lái)的那?因?yàn)槲覜](méi)有寫(xiě)這個(gè)方法。
[CompilerGenerated] private static void <CountAsync> b__0() { for (int index = 0; index < 10; ++index) { Console.WriteLine(index); Thread.Sleep(100); }}看一下方法體,忽然覺(jué)得很眼熟。美女! 我們好像哪里見(jiàn)過(guò)。仔細(xì)想想,這不就是我們寫(xiě)的Lambda 表達(dá)式么,讓我們?cè)賮?lái)看一眼我們的 Lambda 表達(dá)式。原來(lái)我們傳給 TaskRun 的 Action 類(lèi)型的委托轉(zhuǎn)換成了一個(gè)方法。
而且我們也看到了編譯器生成了一個(gè) Action 類(lèi)型委托的字段,估計(jì)就是用來(lái)傳遞這個(gè)自動(dòng)生成的方法的。
await Task.Run(() => { for (int i = 0; i < 10; i++) { Console.WriteLine(i); Thread.Sleep(100); } } );CountAsync ,這個(gè)名字很熟悉,是我聲名的異步方法的名字,并且使用了 async 關(guān)鍵字進(jìn)行了限制 -- private static async void CountAsync()。
但是我聲名的是一個(gè)方法,絕不是一個(gè) struct 呀!為什么編譯成了 struct 那?
難道是 async 這個(gè)東西在作怪,因?yàn)樵谶@個(gè)代碼里再也找不到 async 了,它消失了。不,不是消失了,而是變成一個(gè) Struct, 而且實(shí)現(xiàn)了IAsyncStateMachine接口。
private struct <CountAsync>d__2 : IAsyncStateMachine
那么IAsyncStateMachine 接口是做什么的?百度一下,還是直接去MSDN 英文網(wǎng)站吧,看看 Microsoft 怎么說(shuō)。
http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.iasyncstatemachine(v=vs.110).aspx 上面說(shuō),Represents state machines that are generated for asynchronous methods. This type is intended for compiler use only.
大致意思是(我的英文水平也不咋的,所以每次碰到一些概念呀,我通常比較喜歡讀英文,這樣說(shuō)起來(lái)比較官方了,其實(shí)有點(diǎn) ZB): IAsyncStateMachine 代表了一個(gè)為異步方法生成的狀態(tài)機(jī),它是轉(zhuǎn)么為了編譯設(shè)計(jì)的。代碼不能調(diào)用它。(看到這里我有一個(gè)疑問(wèn),既然專(zhuān)門(mén)設(shè)計(jì)的為什么要聲名為 public)。
暈!還沒(méi)有弄明白IAsyncStateMachine 時(shí)神馬,又來(lái)個(gè)狀態(tài)機(jī) (State Machine) , 這又是什么?忽然間覺(jué)得天旋地轉(zhuǎn),原來(lái)我只是一個(gè)小白,啥也不知道。黑發(fā)不知勤學(xué)早, 白首方悔讀書(shū)遲(廢話02)。
有事問(wèn) Google/Bai du,很多關(guān)于狀態(tài)機(jī)的解釋是數(shù)字電路,恍惚間覺(jué)得我好像在大學(xué)里讀過(guò)這門(mén)課,但是又好像沒(méi)讀過(guò)。(廢話03)
不管了,狀態(tài)機(jī)大致上說(shuō)的是,在有限個(gè)狀態(tài)以及這些狀態(tài)的轉(zhuǎn)移和動(dòng)作等行為的模型。每個(gè)狀態(tài)可以分為進(jìn)入動(dòng)作,退出動(dòng)作等。在編程中有基于事件的狀態(tài)機(jī)。事件,難道這個(gè) struct 使用了事件?且行且學(xué)習(xí)吧!
言歸正傳,IAsyncStateMachine 接口在System.Runtime.CompilerServices命名空間下,有兩個(gè)的方法:
好了,看了了struct 的聲名,看一下它的成員吧,先從字段看起(我們知道 .NET 中的屬性實(shí)際上是對(duì)字段的封裝,編譯器會(huì)對(duì)其做相應(yīng)轉(zhuǎn)化),為了易讀我對(duì)屬性名字做了一些加工,這樣讓各位看官看起來(lái)更舒服吧:
TaskAwaiter, await。 多么的相似呀! 難道它們之間有什么神秘的聯(lián)系!
按照我八卦的思維來(lái)說(shuō),這個(gè)肯定有不可告人的聯(lián)系。(廢話04)
但是我時(shí)程序猿,需要理性的思維,還是看看源代碼吧:
用反射神器看清一下AsyncVoidMethodBuilder。哦,原來(lái)如此!
private Task Task { get { if (this.m_task == null) this.m_task = new Task(); return this.m_task; } }啊!原來(lái) async 是一個(gè)披著漂亮外皮的 Thread。妖怪,哪兒跑!
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { this.m_coreState.Start<TStateMachine>(ref stateMachine);} public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter,ref TStateMachine stateMachine)where TAwaiter : INotifyCompletionwhere TStateMachine : IAsyncStateMachine { try { Action completionAction = this.m_coreState.GetCompletionAction<AsyncVoidMethodBuilder, TStateMachine>(ref this, ref stateMachine); awaiter.OnCompleted(completionAction); } catch (Exception ex) { AsyncMethodBuilderCore.ThrowAsync(ex, (SynchronizationContext) null); }}從上面介紹,MoveNext 方法用來(lái)處理狀態(tài)機(jī)不同狀態(tài)之間的轉(zhuǎn)換。根據(jù) state 不同執(zhí)行不同的操作,實(shí)際上是一個(gè) switch(state) 的語(yǔ)句。
復(fù)雜的事物往往是由簡(jiǎn)單的東西構(gòu)成的,switch 語(yǔ)句,多么熟悉的語(yǔ)句,經(jīng)歷那么多年風(fēng)和雨!
Agile 說(shuō)唯一有說(shuō)服力,且完全正確的東西就是代碼。那么就把完全的反編譯代碼放在下面,大俠們自己閱讀,自己體會(huì)。
[CompilerGenerated] [StructLayout(LayoutKind.Auto)]
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注