自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Await,Async 我要把它翻個底朝天,這回你總該明白了吧

開發(fā) 前端
在 IL 層面都會被打成原型的,所以在這個層面上認識這兩個語法糖是非常有必要的。

[[339203]]

一:背景

1. 講故事

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

二:從 IL 層面認識

1. 使用 WebClient 下載

為了方便打回原型,我先上一個例子,使用 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<string> 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.    } 

 

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

2. 挖掘 await async 的IL代碼

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

 

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

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

因為不知道從哪里冒出來的,特別引人關注,所以看看它的 IL 是咋樣的?

  1. .class nested private auto ansi sealed beforefieldinit '<GetResult>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__1 類實現了接口 IAsyncStateMachine,定義如下:

 

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

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

為了方便演示,我對方法體中的 IL 代碼做一下簡化:

  1. .method private hidebysig static  
  2.     class [System.Runtime]System.Threading.Tasks.Task`1<string> GetResult () cil managed  
  3.     IL_0000: newobj instance void ConsoleApp3.Program/'<GetResult>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<!0> valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string>::Create() 
  7.     IL_000c: stfld valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string> ConsoleApp3.Program/'<GetResult>d__1'::'<>t__builder' 
  8.     IL_0011: ldloc.0 
  9.     IL_0012: ldc.i4.m1 
  10.     IL_0013: stfld int32 ConsoleApp3.Program/'<GetResult>d__1'::'<>1__state' 
  11.     IL_0018: ldloc.0 
  12.     IL_0019: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string> ConsoleApp3.Program/'<GetResult>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<string>::Start<class ConsoleApp3.Program/'<GetResult>d__1'>(!!0&) 
  15.     IL_0025: ldloc.0 
  16.     IL_0026: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string> ConsoleApp3.Program/'<GetResult>d__1'::'<>t__builder' 
  17.     IL_002b: call instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string>::get_Task() 
  18.     IL_0030: ret 
  19. } // end of method Program::GetResult 

如果你稍微懂一點的話,在 IL_0000 處的 newobj 你就應該知道這個方法就是做了 new d__1,然后從 IL_002b 處返回了一個 get_Task() ,這時候你就應該明白,為什么主線程不會被阻塞,因為人家返回的是 Task ,對吧,最后的 http 結果會藏在 Task 中,這樣是不是就很好理解了。

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

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

三:將 IL 代碼 回寫為 C#

1. 完整 C# 代碼

通過前面一部分你應該對 await ,async 在 IL 層面有了一個框架性的認識,這里我就全部反寫成 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<string> GetResult() 
  15.         { 
  16.             GetResult stateMachine = new GetResult(); 
  17.  
  18.             stateMachine.builder = AsyncTaskMethodBuilder<string>.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<string> builder; 
  32.         private WebClient client; 
  33.         private string content; 
  34.         private string s3; 
  35.         private TaskAwaiter<string> awaiter; 
  36.  
  37.         public void MoveNext() 
  38.         { 
  39.             var result = string.Empty; 
  40.             TaskAwaiter<string> 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<string>); 
  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

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

 

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

  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; 

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

然后簡單說一下 狀態(tài)機 的走法,通過調試會發(fā)現這里會走 兩次 MoveNext,一次啟動,一次拿結果。

<1> 第一次回調 MoveNext

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

 

<2> 第二次回調 MoveNext

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

 

四:總結

 

語法糖有簡單和復雜之分,復雜的也不要怕,學會將 IL 代碼翻譯成 C# ,或許你以前很多不明白的地方此時都會豁然開朗,不是嗎?

本文轉載自微信公眾號「一線碼農聊技術」,可以通過以下二維碼關注。轉載本文請聯系一線碼農聊技術公眾號。  一線碼農聊技術

 

責任編輯:武曉燕 來源: 一線碼農聊技術
相關推薦

2018-04-11 06:42:46

個人隱私Facebook數據

2023-12-28 08:43:28

前端算法搜索

2024-03-12 08:37:32

asyncawaitJavaScript

2019-09-19 09:18:02

API網關互聯網

2014-07-15 10:31:07

asyncawait

2022-02-10 09:04:50

架構

2024-04-08 00:00:00

asyncawaiPromise

2011-11-03 10:57:31

IT資產開源軟件

2016-11-22 11:08:34

asyncjavascript

2012-07-22 15:59:42

Silverlight

2021-07-20 10:26:12

JavaScriptasyncawait

2023-10-08 10:21:11

JavaScriptAsync

2022-08-27 13:49:36

ES7promiseresolve

2023-07-28 07:31:52

JavaScriptasyncawait

2024-12-30 08:22:35

2021-06-28 07:27:43

AwaitAsync語法

2024-12-23 08:00:45

2023-03-23 14:20:45

3D

2023-02-27 08:10:16

2022-06-13 07:36:47

useEffectHooks
點贊
收藏

51CTO技術棧公眾號