国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

Async 與 Await 關(guān)鍵字研究

2019-11-17 03:11:37
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

Async 與 Await 關(guān)鍵字研究

1 Aynsc 和 Await 關(guān)鍵字的研究

在 .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ì)我的代碼做了什么,讓她(它)變成這樣了?

好吧,讓我們逐條看一下:

1.1 Main 方法

基本上和我們寫(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();    }

1.2 CountAsync 方法

這個(gè)不是我寫(xiě)的CountAsync 方法么?

我的 async 關(guān)鍵字,我的await Task.Run 都去那兒了?

為了揭開(kāi)這個(gè)謎底,找出真相,真相永遠(yuǎn)只有一個(gè),我們先看一下方法的構(gòu)成:

  • 聲名一個(gè)類(lèi)型為<CountAsync>d_2 類(lèi)型的stateMachine 局部變量
  • 為 stateMachine.t_builder 屬性賦值
  • 將 stateMachine 的 1_state 字段賦值為-1
  • 調(diào)用 stateMachine 的t_builder 成員的 Start 方法。

   [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);    }

1.3 Action delegate – Action 委托

private static Action $__CachedAnonymousMethodDelegate1;

生成了一個(gè) Action 委托,巧合的是,我們?cè)谡{(diào)用 Task.Run 方法啟動(dòng)一個(gè)新的 Task 時(shí),傳的也是一個(gè)Action 委托,難道這是一個(gè)巧合,還是別有用意?

1.4 Async Method- 異步方法

一個(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);                   }               }               );

1.5 CountAsync StateMachine – CountAsync 狀態(tài)機(jī)

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è)的方法:

  • MoveNext – 從一個(gè)狀態(tài)轉(zhuǎn)移到另外一個(gè)狀態(tài)
  • SetStateMachine - 配置狀態(tài)機(jī)堆分配的一個(gè)拷貝

1.5.1 字段

好了,看了了struct 的聲名,看一下它的成員吧,先從字段看起(我們知道 .NET 中的屬性實(shí)際上是對(duì)字段的封裝,編譯器會(huì)對(duì)其做相應(yīng)轉(zhuǎn)化),為了易讀我對(duì)屬性名字做了一些加工,這樣讓各位看官看起來(lái)更舒服吧:

  • Int32 _state – 一個(gè)用于表示狀態(tài)機(jī)狀態(tài)的整數(shù)
    • 0 -- 方法 CountAsync 異步執(zhí)行完成
    • -1 – 第一次執(zhí)行 Async 方法
    • TaskAwaiter _awaiter –設(shè)置等待異步任務(wù)完成時(shí)要執(zhí)行的操作:

TaskAwaiter, await。 多么的相似呀! 難道它們之間有什么神秘的聯(lián)系!

按照我八卦的思維來(lái)說(shuō),這個(gè)肯定有不可告人的聯(lián)系。(廢話04)

但是我時(shí)程序猿,需要理性的思維,還是看看源代碼吧:

  • 嗯,TaskAwaiter 是一個(gè) struct, 實(shí)現(xiàn)了 INotifyCompletion(OnCompleted), ICrtiticalNotifyCompletion(OnUnsafeCompleted)。從這里可以看出 TaskAwaiter 需要對(duì)一個(gè) Task 執(zhí)行完成做出處理,或者說(shuō)對(duì) Thread 的完成事件做相應(yīng)。在接著看,我們發(fā)現(xiàn),OnCompleted 和 OnUnsafeCompleted 均調(diào)用了 Task 類(lèi)的SetContinuationForAwait 方法,類(lèi)似類(lèi) Continue 方法,當(dāng)一個(gè)方法完成后執(zhí)行后續(xù)方法。
  • 另外 TaskAwaiter 有一個(gè)只讀的 Task 屬性,在構(gòu)造函數(shù)中初始化。
  • IsCompleted 屬性,標(biāo)識(shí)其維護(hù)的 Task 是否完成。

  • AsyncVoidMethodBuilder _builder – 用來(lái)創(chuàng)建一個(gè)異步無(wú)返回值的方法。

用反射神器看清一下AsyncVoidMethodBuilder。哦,原來(lái)如此!

  • AsyncVoidMethodBuilder 類(lèi)有一個(gè)私有的 Task 屬性,看到這里,我瞬間好像明白了什么,原來(lái) async 方法會(huì)偷偷的在后臺(tái)為你穿件一個(gè) Task, Task 是什么,本質(zhì)上是一個(gè)線程,Thread。

private Task Task    {      get      {        if (this.m_task == null)          this.m_task = new Task();        return this.m_task;      }           }

啊!原來(lái) async 是一個(gè)披著漂亮外皮的 Thread。妖怪,哪兒跑!

  • Create() 方法 – 創(chuàng)建一個(gè)AsyncVoidMethodBuilder 實(shí)例,注意到AsyncVoidMethodBuilder 是一個(gè) struct 類(lèi)型,值類(lèi)型,值類(lèi)型和引用類(lèi)型的區(qū)別還是很大滴,用一個(gè)靜態(tài)方法創(chuàng)建實(shí)例,為一個(gè)必要的屬性,字段賦值是很到的選擇。
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine    {      this.m_coreState.Start<TStateMachine>(ref stateMachine);} 

  • AwaitOnCompleted()/AwaitOnUnsafeCompleted – 什么,什么,我沒(méi)聽(tīng)錯(cuò)吧!Await + OnCompleted/OnUnsafeCompleted,難道 await 關(guān)鍵字被轉(zhuǎn)換成了TaskAwaiter 對(duì)象的INotifyCompletion 方法 OnCompleted 的調(diào)用。看一下代碼,先!有可能,但是還是需要看下去。
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);      }}

  • SetResult -- 設(shè)置值
  • SetException – 設(shè)置 Exception

1.5.2 MoveNext 方法

從上面介紹,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)和雨!

1.5.3 代碼

Agile 說(shuō)唯一有說(shuō)服力,且完全正確的東西就是代碼。那么就把完全的反編譯代碼放在下面,大俠們自己閱讀,自己體會(huì)。

 [CompilerGenerated]    [StructLayout(LayoutKind.Auto)]
發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 靖西县| 岳西县| 洪洞县| 元氏县| 湖南省| 红桥区| 绥化市| 滦平县| 扶沟县| 蒙城县| 襄汾县| 文山县| 南京市| 义马市| 武平县| 延长县| 中方县| 门源| 定陶县| 陕西省| 山阴县| 兰西县| 浙江省| 虞城县| 湛江市| 河西区| 横山县| 韩城市| 衡阳市| 普宁市| 永丰县| 西乌珠穆沁旗| 安福县| 肃南| 青州市| 嘉义市| 阿鲁科尔沁旗| 鄂托克旗| 保靖县| 三门县| 商河县|