async-await原理解析

? ? 在用async包裹的方法體中,可以使用await關鍵字以同步的方式編寫異步調用的代碼。那么它的內部實現原理是什么樣的呢?我們是否可以自定義await以實現定制性的需求呢?先來看一個簡單的例子:

 1     class Test {
 2         public static void Main (string[] args) {
 3             Task.Run (new Func<Task<string>>(task1));
 4             Console.ReadLine ();
 5         }
 6 
 7         private async static Task<string> task1() {
 8             string ret = await task2 ();
 9             Console.WriteLine ("Await Task Result:" + ret);
10             return ret;
11         }
12 
13         private static Task<string> task2() {
14             return Task.FromResult<string> ("Task2");
15         }
16     }

? ? 通過ILSpy反編譯(要關閉"視圖-選項-反編譯await/async"菜單項),得到如下代碼:

 1     internal class Test
 2     {
 3         [CompilerGenerated]
 4         [StructLayout(LayoutKind.Auto)]
 5         private struct <task1>d__0 : IAsyncStateMachine
 6         {
 7             public int <>1__state;
 9             public AsyncTaskMethodBuilder<string> <>t__builder; 
11             public string <ret>5__1;
13             private TaskAwaiter<string> <>u__$awaiter2;
15             private object <>t__stack;
16 
17             void IAsyncStateMachine.MoveNext()
18             {
19                 string result;
20                 try
21                 {
22                     int num = this.<>1__state;
23                     if (num != -3)
24                     {
25                         TaskAwaiter<string> taskAwaiter;
26                         if (num != 0)
27                         {
28                             taskAwaiter = Test.task2().GetAwaiter();
29                             if (!taskAwaiter.IsCompleted)
30                             {
31                                 this.<>1__state = 0;
32                                 this.<>u__$awaiter2 = taskAwaiter;
33                                 this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, Test.<task1>d__0>(ref taskAwaiter, ref this);
34                                 return;
35                             }
36                         }
37                         else
38                         {
39                             taskAwaiter = this.<>u__$awaiter2;
40                             this.<>u__$awaiter2 = default(TaskAwaiter<string>);
41                             this.<>1__state = -1;
42                         }
43                         string arg_86_0 = taskAwaiter.GetResult();
44                         taskAwaiter = default(TaskAwaiter<string>);
45                         string text = arg_86_0;
46                         this.<ret>5__1 = text;
47                         Console.WriteLine("Await Task Result:" + this.<ret>5__1);
48                         result = this.<ret>5__1;
49                     }
50                 }
51                 catch (Exception exception)
52                 {
53                     this.<>1__state = -2;
54                     this.<>t__builder.SetException(exception);
55                     return;
56                 }
57                 this.<>1__state = -2;
58                 this.<>t__builder.SetResult(result);
59             }
60 
61             [DebuggerHidden]
62             void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)
63             {
64                 this.<>t__builder.SetStateMachine(param0);
65             }
66         }
67 
68         public static void Main(string[] args)
69         {
70             Task.Run<string>(new Func<Task<string>>(Test.task1));
71             Console.ReadLine();
72         }
73 
74         [DebuggerStepThrough, AsyncStateMachine(typeof(Test.<task1>d__0))]
75         private static Task<string> task1()
76         {
77             Test.<task1>d__0 <task1>d__;
78             <task1>d__.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
79             <task1>d__.<>1__state = -1;
80             AsyncTaskMethodBuilder<string> <>t__builder = <task1>d__.<>t__builder;
81             <>t__builder.Start<Test.<task1>d__0>(ref <task1>d__);
82             return <task1>d__.<>t__builder.Task;
83         }
84 
85         private static Task<string> task2()
86         {
87             return Task.FromResult<string>("Task2");
88         }
89     }

? ? ?按照代碼的調用順序,我們關注下task1()的內部實現。

? ? 首先是初始化結構體<task1>d_0的實例<task1>d__。那么<task1>d__0是個什么東東呢?由編譯器的生成代碼中可以看到,它是一個實現了IAsyncStateMachine接口的結構體,而用戶代碼則被編譯器重新組織進了MoveNext()方法中。<task1>d__0有個內部狀態成員<>1__state,MoveNext()方法根據這個狀態調轉到相應的代碼塊中加以執行。

? ? 了解了<task1>d__0的聲明實現,再看下task1()方法中的具體調用。在創建實例<task1>d__之后,設置初始狀態<>1__state為-1,并調用<>t__builder的Start方法。不難推斷,在Start方法中會調用<task1>d__.MoveNext(),此時內部狀態為-1,會先調用Test.task2().GetAwaiter()獲取其所關聯的TaskAwaiter實例。如果awaiter當前是未結束的話,則設置<>1__state為0,并將當前<task1>d__作為參數關聯到TaskAwaiter實例的onCompletedContinuation回調延續中去。當未來某個時刻,TaskAwaiter所關聯的Task任務結束時,會設置awaiter的異步結果并觸發回調延續,導致調用<task1>d__.MoveNext()方法,并最終跳轉到用戶代碼塊中,獲取awaiter的異步結果并交由用戶代碼處理。這個回調,基于Task.ConfigureAwait(true/false)的不同,會在后續切換到當前線程或是從線程池中取了一個空閑線程來處理(更細節可參考.net源碼分析)。

? ? 這里要順便提一句,在本例中,通過Task.Run創建了taskX1,await之后的代碼與taskX1沒有任何關系,從編譯器生成的代碼來看,在調用task1()方法并調用<task1>d__.Start()方法之后taskX便結束了,雖然task1()方法返回了新的Task<string>實例,但是只是特定類型的返回值而已,與taskX1或Task沒有任何關系。

? ? 由以上分析可以看到,async/await只是一個語法糖,async告知編譯器要生成狀態機代碼,await則是配合生成GetAwaiter(),并封裝跳轉的用戶代碼塊。除此之外,async/await與Task沒有任何直接關系。而TaskAwaiter的作用,是實現INotifyCompletion(在System.Runtime.CompilerServices命名空間)以橋接異步回調過程。那么第二個自定義await的問題便一目了然了:任何類型,只需要實現GetAwaiter()方法以返回INotifyCompletion實例,便可以被await。

? ? 舉個例子:

 1     class TestAwaiter<T> : INotifyCompletion {
 2         private T result;
 3         private Action continuation;
 4 
 5         // INotifyCompletion Implement
 6         public void OnCompleted(Action continuation) { this.continuation = continuation; }
 7 
 8         // Compiler Call Methods
 9         public bool IsCompleted { get; private set; }
10         public T GetResult() { return result; }
11         public TestAwaiter<T> GetAwaiter() { return this; }

// Self Call Methods
12 public void SetResult(T ret) { 13 result = ret; 14 if (continuation != null) { 15 continuation (); 16 } 17 } 18 } 19 20 class Test { 21 public static void Main (string[] args) { 22 Task.Run (new Action(task1)); 23 Console.ReadLine (); 24 } 25 26 private async static void task1() { 27 Console.WriteLine ("Begin await:"); 28 int ret = await testAwaiter (); 29 Console.WriteLine ("Await Task Result:" + ret); 30 } 31 32 private static TestAwaiter<int> testAwaiter() { 33 TestAwaiter<int> awaiter = new TestAwaiter<int> (); 34 ThreadPool.QueueUserWorkItem (_ => { 35 Thread.Sleep(3000); 36 awaiter.SetResult (100); 37 }); 38 return awaiter; 39 } 40 }

? ? 這里沒有再定義單獨的類型以返回TestAwaiter,而是把二者都封裝在了TestAwaiter內部。運行結果如下:

? ? Begin await:

? ? Await Task Result:100

轉載于:https://www.cnblogs.com/Jackie-Snow/p/6542372.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/259404.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/259404.shtml
英文地址,請注明出處:http://en.pswp.cn/news/259404.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

emacs-w3m查看html幫助手冊

<?xml version"1.0" encoding"utf-8"?> emacs-w3m查看html幫助手冊emacs-w3m查看html幫助手冊 Table of Contents 1. 使用效果2. 為什么要用emacs-w3m來查看html的幫助手冊&#xff1f;3. 什么是w3m?4. 配置5. 額外資源1 使用效果 使用快捷鍵C-c …

c語言生命游戲代碼大全,c++生命游戲源碼

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓glViewport( 0, 0, width, height );glMatrixMode( GL_PROJECTION );glLoadIdentity( );}//程序入口int main(int argc, char *argv[]){//隨機生成細胞的狀態MapRand();std::cout<//SDL初始化const SDL_VideoInfo* info NULL;i…

初學React,setState后獲取到的thisstate沒變,還是初始state?

問題&#xff1a;(javascript)初學React&#xff0c;setState后獲取到的thisstate沒變&#xff0c;還是初始state&#xff1f;描述: getInitialState(){return {data:[]};},componentDidMount(){var data [ { author: "Pete Hunt", text: "This is one comment…

sizeof(數組名)和sizeof(指針)

轉載&#xff1a;http://blog.csdn.net/kangroger/article/details/20653255 在做這道題時&#xff1a; 32位環境下&#xff0c;int *pnew int[10];請問sizeof(p)的值為&#xff08;&#xff09; A、4 B、10 C、40 D、8 我以為正確答…

工作中的問題

今天寫一專題頁面&#xff0c;寫出的結果在各個瀏覽器下都不同&#xff0c;心情不好。。。 就是紅線的地方老對不齊。。。 在朋友指導下改了下樣式好了 右邊代碼結構 1 <div class"fr Img"> 2 <h3>相關專題</h3> 3 <a href"#"…

數組的sizeof

轉載&#xff1a;http://blog.163.com/chen_xinghuan/blog/static/17220158220112182838196/ 數組的sizeof值等于數組所占用的內存字節數&#xff0c;如&#xff1a;   char a1[] “abc”;   int a2[3];   sizeof( a1 ); // 結果為4&#xff0c;字符 末尾還存在一個…

數據結構行編輯成簇 c語言,索引的數據結構及底層存儲

索引是幫助數據庫高效獲取數據的數據結構索引的數據結構1.hash表a.利用hash存儲的話需要將所有的數據文件添加到內存&#xff0c;比較耗費內存空間b.hash表存儲的是無序數據&#xff0c;范圍查找的時候需要挨個進行遍歷&#xff0c;比較耗費時間。2.二叉樹二叉樹規定左子樹必須…

卓同學的 Swift 面試題

我覺得應該掌握的知識點&#xff0c;沒有實際意義。 class 和 struct 的區別不通過繼承&#xff0c;代碼復用&#xff08;共享&#xff09;的方式有哪些Set 獨有的方法有哪些&#xff1f;實現一個 min 函數&#xff0c;返回兩個元素較小的元素map、filter、reduce 的作用map 與…

使用CImage雙緩沖

一普通顯示&#xff1a;現在的VC顯示圖片非常方便&#xff0c;遠不是VC6.0那個年代的技術可比&#xff0c;而且支持多種格式的如JPG&#xff0c;PNG。 CImage _img; 初始化&#xff1a; _img.Load(L"map.png"); 顯示&#xff1a;OnPaint事件中 CRect rect; this…

匯編語言學習系列 for循環實現

假如匯編語言要實現如下C語言的功能&#xff0c;編譯環境Ubuntu14.04&#xff08;32位&#xff09;。 #include<stdio.h> int fact_for(int n) {int i;int result 1;for(i 2; i < n; i)result * i;return result; }int main(){printf("%d\n", fact_for(3)…

川大錦城c語言期末考試答案,四川大學《計算機組成原理》2018期末考試B卷答案及評分標準.doc...

四川大學期末考試試題(閉卷)答案及評分標準(2017——2018學年第 2 學期) B卷課程號&#xff1a;304036030 課程名稱&#xff1a;計算機組成原理填空題(本大題共15空&#xff0c;每空2分&#xff0c;共30分)在評價計算機性能時用 響應時間 表示計算機完成某任務所需時間;用 吞吐…

2014屆華為校園招聘機試題2

第一題、輸入一個正整數&#xff0c;并編碼為字符串進行輸出 描述: 1、輸入一個正整數&#xff0c;并編碼為字符串進行輸出。 編碼規則為&#xff1a;數字0-9分別編碼為字符a-j 2、輸入肯定是正整數&#xff0c;不用做錯誤較驗 運行時間限制: 無限制 內存限制: 無限制 輸…

圖解phpstorm常用快捷鍵

查詢快捷鍵 CTRLN 查找類 CTRLSHIFTN 全局搜索文件 ,優先文件名匹配的文件 CTRLSHIFTALTN 查找php類名/變量名 ,js方法名/變量名, css 選擇器 CIRLB 找變量的來源&#xff0c;跳到變量申明處 (CTRL 鼠標單擊 也可以) CTRLALTB 找到繼承該接口或者父級 的所有子類, 統計所有子類…

The C Programming Language--可變參數的函數

函數 printf的正確聲明形式為&#xff1a;int printf(char *fmt, ...) void va_start (va list ap, last-required) type va_arg (va list ap, type) void va_end (va list ap) 其中&#xff0c;省略號表示參數表中參數的數量和類型是可變的。 va_list 類型用于聲明一個變量&am…

二分查找法的循環與遞歸實現及時間復雜度分析

轉載&#xff1a;http://baike.baidu.com/link?url3aEK-qcVbYi6ioJOsf-dFmvFQ6WQgzTwnE9JkmlHBc88qk-D00SambfrSl3hVh_UyqyxF8QEUosfq20IQQW5z_ 和http://hi.baidu.com/networkor/item/80d817f8331d8e08a7298834 設數組為整數數組&#xff0c;從小到大排序。二分法強調一定是…

cifar10 c語言,Python3讀取深度學習CIFAR-10數據集出現的若干問題解決

今天在看網上的視頻學習深度學習的時候&#xff0c;用到了CIFAR-10數據集。當我興高采烈的運行代碼時&#xff0c;卻發現了一些錯誤&#xff1a;# -*- coding: utf-8 -*-import pickle as pimport numpy as np import os def load_CIFAR_batch(filename): """ 載…

Java程序性能優化

一、避免在循環條件中使用復雜表達式 在不做編譯優化的情況下&#xff0c;在循環中&#xff0c;循環條件會被反復計算&#xff0c;如果不使用復雜表達式&#xff0c;而使循環條件值不變的話&#xff0c;程序將會運行的更快。 例子&#xff1a; import java.util.vector; class …

asp.net表單提交方法:GET\POST介紹

表單form的提交有兩種方式&#xff0c;一種是get的方法&#xff0c;一種是post 的方法&#xff0c;如果沒有特殊指定&#xff0c;默認為post。看下面代碼,理解ASP.NET Get和Post兩種提交的區別: 1.< form id"form1" method"get" runat"server"…

各種排序算法總結

轉載&#xff1a;http://blog.csdn.net/warringah1/article/details/8951220 明天就要去參加阿里巴巴的實習生筆試了&#xff0c;雖然沒想著能進去&#xff0c;但是態度還是要端正的&#xff0c;也沒什么可以準備的&#xff0c;復習復習排序吧。 1 插入排序 void InsertSort(in…

CentOS7 上安裝 Zookeeper-3.4.9 服務

在 CentOS7 上安裝 zookeeper-3.4.9 服務1、創建 /usr/local/services/zookeeper 文件夾&#xff1a; mkdir -p /usr/local/services/zookeeper 2、進入到 /usr/local/services/zookeeper 目錄中&#xff1a; cd /usr/local/services/zookeeper 3、下載 zookeeper-3.4.9.…