日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Await,Async 我要把它翻個(gè)底朝天,這回你總該明白了吧

一:背景

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對(duì)這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡(jiǎn)單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:主機(jī)域名、網(wǎng)頁空間、營(yíng)銷軟件、網(wǎng)站建設(shè)、黎川網(wǎng)站維護(hù)、網(wǎng)站推廣。

1. 講故事

await,async 這玩意的知識(shí)點(diǎn)已經(jīng)被人說的爛的不能再爛了,看似沒什么好說的,但我發(fā)現(xiàn)有不少文章還是從理論上講述了這兩個(gè)語法糖的用法,懂得還是懂,不懂的看似懂了過幾天又不懂了,人生如戲全靠記是不行的哈,其實(shí)本質(zhì)上來說 await, async 只是編譯器層面上的語法糖,在 IL 層面都會(huì)被打成原型的,所以在這個(gè)層面上認(rèn)識(shí)這兩個(gè)語法糖是非常有必要的。

二:從 IL 層面認(rèn)識(shí)

1. 使用 WebClient 下載

為了方便打回原型,我先上一個(gè)例子,使用 webclient 異步下載 http://cnblogs.com 的html,代碼如下:

 
 
 
 
  1. class Program 
  2.    { 
  3.        static void Main(string[] args) 
  4.        { 
  5.            var html = GetResult(); 
  6.  
  7.            Console.WriteLine("稍等... 正在下載 cnblogs -> html \r\n"); 
  8.  
  9.            var content = html.Result; 
  10.  
  11.            Console.WriteLine(content); 
  12.        } 
  13.  
  14.        static async Task GetResult() 
  15.        { 
  16.            var client = new WebClient(); 
  17.  
  18.            var content = await client.DownloadStringTaskAsync(new Uri("http://cnblogs.com")); 
  19.  
  20.            return content; 
  21.        } 
  22.    } 

上面的代碼非常簡(jiǎn)單,可以看到異步操作沒有阻塞主線程輸出: 稍等... 正在下載 cnblogs -> html \r\n, 編譯器層面沒什么好說的 ,接下來看下在 IL 層面發(fā)生了什么?

2. 挖掘 await async 的IL代碼

還是老規(guī)矩, ilSpy 走起,如下圖:

可以看到,這里有一個(gè) GetResult 方法 ,一個(gè) Main 方法,還有一個(gè)不知道在哪里冒出來的 d__1 類,接下來和大家一個(gè)一個(gè)聊。

<1 style="box-sizing: border-box;"> \d__1> 類

因?yàn)椴恢缽哪睦锩俺鰜淼?,特別引人關(guān)注,所以看看它的 IL 是咋樣的?

 
 
 
 
  1. .class nested private auto ansi sealed beforefieldinit 'd__1' 
  2.     extends [System.Runtime]System.Object 
  3.     implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine 
  4.     .method private final hidebysig newslot virtual  
  5.         instance void MoveNext () cil managed 
  6.     { 
  7.     } 
  8.  
  9.     .method private final hidebysig newslot virtual  
  10.         instance void SetStateMachine ( 
  11.             class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine 
  12.         ) cil managed 
  13.     { 
  14.  
  15.     } 

從上面的 IL 代碼可以看到,這是自動(dòng)生成的 d__1 類實(shí)現(xiàn)了接口 IAsyncStateMachine,定義如下:

看到里面的 MoveNext 是不是很眼熟,平時(shí)你在 foreach 集合的時(shí)候就會(huì)用到這個(gè)方法,那時(shí)人家叫做枚舉類,在這里算是被改造了一下, 叫狀態(tài)機(jī)。

<2 style="box-sizing: border-box;"> GetResult ()

為了方便演示,我對(duì)方法體中的 IL 代碼做一下簡(jiǎn)化:

 
 
 
 
  1. .method private hidebysig static  
  2.     class [System.Runtime]System.Threading.Tasks.Task`1 GetResult () cil managed  
  3.     IL_0000: newobj instance void ConsoleApp3.Program/'d__1'::.ctor() 
  4.     IL_0005: stloc.0 
  5.     IL_0006: ldloc.0 
  6.     IL_0007: call valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::Create() 
  7.     IL_000c: stfld valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 ConsoleApp3.Program/'d__1'::'<>t__builder' 
  8.     IL_0011: ldloc.0 
  9.     IL_0012: ldc.i4.m1 
  10.     IL_0013: stfld int32 ConsoleApp3.Program/'d__1'::'<>1__state' 
  11.     IL_0018: ldloc.0 
  12.     IL_0019: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 ConsoleApp3.Program/'d__1'::'<>t__builder' 
  13.     IL_001e: ldloca.s 0 
  14.     IL_0020: call instance void valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::Startd__1'>(!!0&) 
  15.     IL_0025: ldloc.0 
  16.     IL_0026: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 ConsoleApp3.Program/'d__1'::'<>t__builder' 
  17.     IL_002b: call instance class [System.Runtime]System.Threading.Tasks.Task`1 valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::get_Task() 
  18.     IL_0030: ret 
  19. } // end of method Program::GetResult 

如果你稍微懂一點(diǎn)的話,在 IL_0000 處的 newobj 你就應(yīng)該知道這個(gè)方法就是做了 new d__1,然后從 IL_002b 處返回了一個(gè) get_Task() ,這時(shí)候你就應(yīng)該明白,為什么主線程不會(huì)被阻塞,因?yàn)槿思曳祷氐氖?Task ,對(duì)吧,最后的 http 結(jié)果會(huì)藏在 Task 中,這樣是不是就很好理解了。

<3 style="box-sizing: border-box;"> Main

Main方法沒有做任何改變,原來是什么樣現(xiàn)在還是什么樣。

三:將 IL 代碼 回寫為 C#

1. 完整 C# 代碼

通過前面一部分你應(yīng)該對(duì) await ,async 在 IL 層面有了一個(gè)框架性的認(rèn)識(shí),這里我就全部反寫成 C# 代碼:

 
 
 
 
  1. class Program 
  2.     { 
  3.         static void Main(string[] args) 
  4.         { 
  5.             var html = GetResult(); 
  6.  
  7.             Console.WriteLine("稍等... 正在下載 cnblogs -> html \r\n"); 
  8.  
  9.             var content = html.Result; 
  10.  
  11.             Console.WriteLine(content); 
  12.         } 
  13.  
  14.         static Task GetResult() 
  15.         { 
  16.             GetResult stateMachine = new GetResult(); 
  17.  
  18.             stateMachine.builder = AsyncTaskMethodBuilder.Create(); 
  19.  
  20.             stateMachine.state = -1; 
  21.  
  22.             stateMachine.builder.Start(ref stateMachine); 
  23.  
  24.             return stateMachine.builder.Task; 
  25.         } 
  26.     } 
  27.  
  28.     class GetResult : IAsyncStateMachine 
  29.     { 
  30.         public int state; 
  31.         public AsyncTaskMethodBuilder builder; 
  32.         private WebClient client; 
  33.         private string content; 
  34.         private string s3; 
  35.         private TaskAwaiter awaiter; 
  36.  
  37.         public void MoveNext() 
  38.         { 
  39.             var result = string.Empty; 
  40.             TaskAwaiter localAwaiter; 
  41.             GetResult stateMachine; 
  42.  
  43.             int num = state; 
  44.  
  45.             try 
  46.             { 
  47.                 if (num == 0) 
  48.                 { 
  49.                     localAwaiter = awaiter; 
  50.                     awaiter = default(TaskAwaiter); 
  51.                     num = state = -1; 
  52.                 } 
  53.                 else 
  54.                 { 
  55.                     client = new WebClient(); 
  56.  
  57.                     localAwaiter = client.DownloadStringTaskAsync(new Uri("http://cnblogs.com")).GetAwaiter(); 
  58.  
  59.                     if (!localAwaiter.IsCompleted) 
  60.                     { 
  61.                         num = state = 0; 
  62.                         awaiter = localAwaiter; 
  63.                         stateMachine = this; 
  64.                         builder.AwaitUnsafeOnCompleted(ref localAwaiter, ref stateMachine); 
  65.                         return; 
  66.                     } 
  67.                 } 
  68.  
  69.                 s3 = localAwaiter.GetResult(); 
  70.                 content = s3; 
  71.                 s3 = null; 
  72.                 result = content; 
  73.             } 
  74.             catch (Exception exx) 
  75.             { 
  76.                 state = -2; 
  77.                 client = null; 
  78.                 content = null; 
  79.                 builder.SetException(exx); 
  80.             } 
  81.  
  82.             state = -2; 
  83.             client = null; 
  84.             content = null; 
  85.             builder.SetResult(result); 
  86.         } 
  87.  
  88.         public void SetStateMachine(IAsyncStateMachine stateMachine) { } 
  89.     } 

可以看到,回寫成 C# 代碼之后跑起來是沒有任何問題的,為了方便理解,我先來畫一張流程圖。

通過上面的 xmind,它基本流程就是: stateMachine.builder.Start(ref stateMachine) -> GetResult.MoveNext -> client.DownloadStringTaskAsync -> localAwaiter.IsCompleted = false -> builder.AwaitUnsafeOnCompleted(ref localAwaiter, ref stateMachine) -> GetResult.MoveNext -> localAwaiter.GetResult() -> builder.SetResult(result)

2. 剖析 AsyncTaskMethodBuilder

其實(shí)你仔細(xì)觀察會(huì)發(fā)現(xiàn),所謂的 await,async 的異步化運(yùn)作都是由 AsyncTaskMethodBuilder 承載的,如異步任務(wù)的啟動(dòng),對(duì)html結(jié)果的封送,接觸底層IO,其中 Task 對(duì)應(yīng)著 AsyncTaskMethodBuilder , Task 對(duì)應(yīng)著 AsyncTaskMethodBuilder, 這也是為什么編譯器在 async 處一直提示你返回 Task 和 Task ,如果不這樣的話的就找不到對(duì)應(yīng) AsyncTaskMethodBuilder 了,對(duì)吧,如下圖:

然后著重看下 AwaitUnsafeOnCompleted 方法,這個(gè)方法非常重要,其注釋如下:

 
 
 
 
  1. // 
  2.         // Summary: 
  3.         //     Schedules the state machine to proceed to the next action when the specified 
  4.         //     awaiter completes. This method can be called from partially trusted code. 
  5.         public void AwaitUnsafeOnCompleted<[NullableAttribute(0)] TAwaiter, [NullableAttribute(0)] TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) 
  6.             where TAwaiter : ICriticalNotifyCompletion 
  7.             where TStateMachine : IAsyncStateMachine; 

一旦調(diào)用了這個(gè)方法,就需要等待 底層IO 將任務(wù)處理完畢之后二次回調(diào) GetResult.MoveNext,也就表示要么異常要么完成任務(wù), Awaiter 包裝的 Task 結(jié)果封送到 builder.SetResult。

然后簡(jiǎn)單說一下 狀態(tài)機(jī) 的走法,通過調(diào)試會(huì)發(fā)現(xiàn)這里會(huì)走 兩次 MoveNext,一次啟動(dòng),一次拿結(jié)果。

<1> 第一次回調(diào) MoveNext

第一次 MoveNext 的觸發(fā)由 stateMachine.builder.Start(ref stateMachine) 發(fā)起,可以用 dnspy 去調(diào)試一下,如下圖:

<2> 第二次回調(diào) MoveNext

第二次 MoveNext 的觸發(fā)由 builder.AwaitUnsafeOnCompleted(ref localAwaiter, ref stateMachine) 開始,可以看到一旦 網(wǎng)絡(luò)驅(qū)動(dòng)程序 處理完畢后就由線程池IO線程主動(dòng)發(fā)起到最后觸發(fā)代碼中的 MoveNext,最后就是到 awaiter 中獲取 task 的 result 處結(jié)束,如下圖:

四:總結(jié)

語法糖有簡(jiǎn)單和復(fù)雜之分,復(fù)雜的也不要怕,學(xué)會(huì)將 IL 代碼翻譯成 C# ,或許你以前很多不明白的地方此時(shí)都會(huì)豁然開朗,不是嗎?

本文轉(zhuǎn)載自微信公眾號(hào)「一線碼農(nóng)聊技術(shù)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系一線碼農(nóng)聊技術(shù)公眾號(hào)。  一線碼農(nóng)聊技術(shù)


網(wǎng)頁名稱:Await,Async 我要把它翻個(gè)底朝天,這回你總該明白了吧
文章鏈接:http://www.dlmjj.cn/article/cdihjoe.html