一. 深度剖析
准備:
先給VS安裝一個插件ILSpy,這樣更容易反編譯代碼進行查看,另外要注意反編譯async和await的時候,要把C#代碼版本改為4.0哦。
1.什么是狀態機
(1).含義:通常我們所說的狀態機(State Machine)指的是有限狀態自動機的簡稱,是現實事物運行規則抽象而成的一個數學模型,可以理解成一個狀態轉換圖。(狀態機是計算機科學的重要基礎概念之一,也可以說是一種總結歸納問題的思想,應用范圍非常廣泛)
(2).例子:自動門有兩個狀態,open 和 closed ,closed 狀態下,如果讀取開門信號,那么狀態就會切換為 open 。open 狀態下如果讀取關門信號,狀態就會切換為 closed .
(3).涉及到4個相關概念:
A.狀態(State):一個狀態機至少包括兩個狀態.(例如上面自動門的例子,有 open 和 closed 兩個狀態。)
B.事件(Event):事件就是執行某個操作的觸發條件或者口令.(對於自動門,“按下開門按鈕”就是一個事件。)
C.動作(Action):事件發生以后要執行的動作,一個action對應一個函數.(事件是“按開門按鈕”,動作是“開門”)
D.變換(Transition):從一個狀態轉換成另外一個狀態.(“開門過程”就是一個變換。)
(4). C#的狀態機提供了IAsyncStateMachine接口,里面有MoveNext 和 SetStateMachine方法處理相應業務.
2. 狀態機分析
async關鍵字標記方法是一個異步方法,編譯器通過這個標記去改造這個方法體為創建狀態機的方法。await是關鍵字是為了實現狀態機中的一個狀態, 每當有一個await,就會生成一個對應的狀態。狀態機就是根據這個狀態,去一步步的調用異步委托,然后回調,包括狀態機的解析。
(1).狀態機的默認狀態都是-1, 結束狀態都是-2.
(2).每await一次就會產生一個 TaskAwaiter<int> awaiter; 改變狀態機的狀態, 當有多個await的時候,每個await都會改變狀態機的狀態,比如 改為 0,1,2,3,4 等等, 分別表示 代碼中await xxx 這句話執行完成。
(3).狀態機的執行套路:
A. 首先創建一個 <xxx>d_num 的方法, xxx代表方法名,num可能是0,1,2,3等,實現IAsyncStateMachine接口。
B. 在MoveNext方法中, 源代碼中每個 await xxxx 都會對應生成是一個 TaskAwaiter<int> awaiter,然后 xxxx.GetAwaiter()
C. 判斷狀態機是否執行完if (!awaiter.IsCompleted),沒有執行完的話走 <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); 代表釋放當前線程
D. 執行完后走,<>s__1 = awaiter.GetResult(); 拿到返回值,繼續走后面的代碼。
(此處寫的比較抽象,看下面3 結合代碼編譯再分析)
3. 結合代碼編譯分析
前提:准備1個Index方法,我們把它當做主方法,在該方法里面調用 F1Async-F5Async這五個方法. (要補充截圖這里)
代碼分享:

public class Home2Controller : Controller { /// <summary> /// 該方法為主方法,用於調用下面的F1-F5的方法 /// </summary> /// <returns></returns> public async Task<IActionResult> Index() { await F1Async(); await F2Async(); await F3Async(); await F4Async(); await F5Async(); return View(); } /// <summary> /// 沒有加async和await的方法 /// (也是一個計算密集型的異步方法,只是編譯的時候本身不會被編譯成狀態機) /// </summary> /// <returns></returns> public static Task<int> F1Async() { return Task.Run(() => { return 2; }); } /// <summary> /// 只要標記了async 就會被編譯成狀態機 /// 如果方法聲明為 async,那么可以直接 return 具體的值,不再用創建Task,由編譯器創建 Task: /// </summary> /// <returns></returns> public static async Task<int> F2Async() { return 2; } /// <summary> /// 計算密集型的異步方法 /// (方法本身也會被編譯成狀態機) /// </summary> /// <returns></returns> public static async Task<int> F3Async() { return await Task.Run(() => { return 2; }); } /// <summary> /// I/O密集型的異步方法 /// </summary> /// <returns></returns> public async Task<int> F4Async() { AsyncDBContext context = new AsyncDBContext(); for (int i = 0; i < 10000; i++) { UserInfor uInfor = new UserInfor() { id = Guid.NewGuid().ToString("N"), userName = "ypf", addTime = DateTime.Now }; await context.AddAsync(uInfor); } return await context.SaveChangesAsync(); } /// <summary> /// 沒有創建狀態機,但是new 了1個新的 task /// </summary> /// <returns></returns> public static Task<int> F5Async() { //內部是new Task<TResult>(result) return Task.FromResult(3); } }
(1).F1Async:沒有加async和await,但它本身也是一個計算密集型的異步方法,該方法本身不會被編譯成狀態機,但調用它的方法Index會被編譯成狀態機。
(2).F2Async:只加了async,會生成狀態機,但由於沒有加await所以不會涉及到中間狀態的變化,從-1默認狀態 變為 結束的-2狀態。
代碼分享:

1 public class Home2Controller : Controller 2 { 3 4 /// <summary> 5 /// 該方法為主方法,用於調用下面的F1-F5的方法 6 /// </summary> 7 /// <returns></returns> 8 public async Task<IActionResult> Index() 9 { 10 await F1Async(); 11 await F2Async(); 12 await F3Async(); 13 await F4Async(); 14 await F5Async(); 15 16 return View(); 17 } 18 19 /// <summary> 20 /// 沒有加async和await的方法 21 /// (也是一個計算密集型的異步方法,只是編譯的時候本身不會被編譯成狀態機) 22 /// </summary> 23 /// <returns></returns> 24 public static Task<int> F1Async() 25 { 26 return Task.Run(() => 27 { 28 return 2; 29 }); 30 } 31 32 /// <summary> 33 /// 只要標記了async 就會被編譯成狀態機 34 /// 如果方法聲明為 async,那么可以直接 return 具體的值 35 /// <returns></returns> 36 public static async Task<int> F2Async() 37 { 38 return 2; 39 } 40 41 /// <summary> 42 /// 計算密集型的異步方法 43 /// (方法本身也會被編譯成狀態機) 44 /// </summary> 45 /// <returns></returns> 46 public static async Task<int> F3Async() 47 { 48 return await Task.Run(() => 49 { 50 return 2; 51 }); 52 } 53 54 /// <summary> 55 /// I/O密集型的異步方法 56 /// </summary> 57 /// <returns></returns> 58 public async Task<int> F4Async() 59 { 60 AsyncDBContext context = new AsyncDBContext(); 61 for (int i = 0; i < 10000; i++) 62 { 63 UserInfor uInfor = new UserInfor() 64 { 65 id = Guid.NewGuid().ToString("N"), 66 userName = "ypf", 67 addTime = DateTime.Now 68 }; 69 await context.AddAsync(uInfor); 70 } 71 return await context.SaveChangesAsync(); 72 } 73 74 75 /// <summary> 76 /// 沒有創建狀態機,但是new 了1個新的 task 77 /// </summary> 78 /// <returns></returns> 79 public static Task<int> F5Async() 80 { 81 //內部是new Task<TResult>(result) 82 return Task.FromResult(3); 83 } 84 85 }
核心代碼剖析:
(3).F3Async:既有async也有await (await只有1個),該方法是使用了Task.Run,我們把它歸為計算型的異步方法。
代碼分享:

[AsyncStateMachine(typeof(<F3Async>d__3))] [DebuggerStepThrough] public static Task<int> F3Async() { <F3Async>d__3 stateMachine = new <F3Async>d__3(); stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create(); stateMachine.<>1__state = -1; AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder; <>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } [CompilerGenerated] private sealed class <F3Async>d__3 : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder<int> <>t__builder; private int <>s__1; private TaskAwaiter<int> <>u__1; private void MoveNext() { int num = <>1__state; int result; try { TaskAwaiter<int> awaiter; if (num != 0) { awaiter = Task.Run(() => 2).GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter; <F3Async>d__3 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } } else { awaiter = <>u__1; <>u__1 = default(TaskAwaiter<int>); num = (<>1__state = -1); } <>s__1 = awaiter.GetResult(); result = <>s__1; } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); return; } <>1__state = -2; <>t__builder.SetResult(result); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } }
核心代碼剖析:
(4).F4Async:既有async又有await,且兩個await,兩個await按照順序執行。
代碼分享:

[AsyncStateMachine(typeof(<F4Async>d__4))] [DebuggerStepThrough] public Task<int> F4Async() { <F4Async>d__4 stateMachine = new <F4Async>d__4(); stateMachine.<>4__this = this; stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create(); stateMachine.<>1__state = -1; AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder; <>t__builder.Start<<F4Async>d__4>(ref stateMachine); return stateMachine.<>t__builder.get_Task(); } [CompilerGenerated] private sealed class <F4Async>d__4 : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder<int> <>t__builder; public Home2Controller <>4__this; private AsyncDBContext <context>5__1; private int <i>5__2; private UserInfor <uInfor>5__3; private int <>s__4; private ValueTaskAwaiter<EntityEntry<UserInfor>> <>u__1; private TaskAwaiter<int> <>u__2; private void MoveNext() { int num = <>1__state; int result; try { ValueTaskAwaiter<EntityEntry<UserInfor>> awaiter; if (num == 0) { awaiter = <>u__1; <>u__1 = default(ValueTaskAwaiter<EntityEntry<UserInfor>>); num = (<>1__state = -1); goto IL_00e8; } if (num != 1) { <context>5__1 = new AsyncDBContext(); <i>5__2 = 0; goto IL_010a; } TaskAwaiter<int> awaiter2 = <>u__2; <>u__2 = default(TaskAwaiter<int>); num = (<>1__state = -1); goto IL_0188; IL_00e8: awaiter.GetResult(); <uInfor>5__3 = null; <i>5__2++; goto IL_010a; IL_010a: if (<i>5__2 < 10000) { <uInfor>5__3 = new UserInfor { id = Guid.NewGuid().ToString("N"), userName = "ypf", addTime = DateTime.Now }; awaiter = ((DbContext)<context>5__1).AddAsync<UserInfor>(<uInfor>5__3, default(CancellationToken)).GetAwaiter(); if (!awaiter.get_IsCompleted()) { num = (<>1__state = 0); <>u__1 = awaiter; <F4Async>d__4 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted<ValueTaskAwaiter<EntityEntry<UserInfor>>, <F4Async>d__4>(ref awaiter, ref stateMachine); return; } goto IL_00e8; } awaiter2 = ((DbContext)<context>5__1).SaveChangesAsync(default(CancellationToken)).GetAwaiter(); if (!awaiter2.get_IsCompleted()) { num = (<>1__state = 1); <>u__2 = awaiter2; <F4Async>d__4 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <F4Async>d__4>(ref awaiter2, ref stateMachine); return; } goto IL_0188; IL_0188: <>s__4 = awaiter2.GetResult(); result = <>s__4; } catch (Exception exception) { <>1__state = -2; <context>5__1 = null; <>t__builder.SetException(exception); return; } <>1__state = -2; <context>5__1 = null; <>t__builder.SetResult(result); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } }
核心代碼剖析:
(5).F5Async:沒有async和await,沒有創建狀態機,但是new 了1個新的 task。
(6).Index:多個await,通過goto 一步一步跳轉,按順序執行。
代碼分享:

[CompilerGenerated] private sealed class <Index>d__0 : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder<IActionResult> <>t__builder; public Home2Controller <>4__this; private TaskAwaiter<int> <>u__1; private void MoveNext() { int num = <>1__state; IActionResult result; try { TaskAwaiter<int> awaiter5; TaskAwaiter<int> awaiter4; TaskAwaiter<int> awaiter3; TaskAwaiter<int> awaiter2; TaskAwaiter<int> awaiter; switch (num) { default: awaiter5 = F1Async().GetAwaiter(); if (!awaiter5.get_IsCompleted()) { num = (<>1__state = 0); <>u__1 = awaiter5; <Index>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter5, ref stateMachine); return; } goto IL_0091; case 0: awaiter5 = <>u__1; <>u__1 = default(TaskAwaiter<int>); num = (<>1__state = -1); goto IL_0091; case 1: awaiter4 = <>u__1; <>u__1 = default(TaskAwaiter<int>); num = (<>1__state = -1); goto IL_00f3; case 2: awaiter3 = <>u__1; <>u__1 = default(TaskAwaiter<int>); num = (<>1__state = -1); goto IL_0155; case 3: awaiter2 = <>u__1; <>u__1 = default(TaskAwaiter<int>); num = (<>1__state = -1); goto IL_01bd; case 4: { awaiter = <>u__1; <>u__1 = default(TaskAwaiter<int>); num = (<>1__state = -1); break; } IL_01bd: awaiter2.GetResult(); awaiter = F5Async().GetAwaiter(); if (!awaiter.get_IsCompleted()) { num = (<>1__state = 4); <>u__1 = awaiter; <Index>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter, ref stateMachine); return; } break; IL_0091: awaiter5.GetResult(); awaiter4 = F2Async().GetAwaiter(); if (!awaiter4.get_IsCompleted()) { num = (<>1__state = 1); <>u__1 = awaiter4; <Index>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter4, ref stateMachine); return; } goto IL_00f3; IL_0155: awaiter3.GetResult(); awaiter2 = <>4__this.F4Async().GetAwaiter(); if (!awaiter2.get_IsCompleted()) { num = (<>1__state = 3); <>u__1 = awaiter2; <Index>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter2, ref stateMachine); return; } goto IL_01bd; IL_00f3: awaiter4.GetResult(); awaiter3 = F3Async().GetAwaiter(); if (!awaiter3.get_IsCompleted()) { num = (<>1__state = 2); <>u__1 = awaiter3; <Index>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter3, ref stateMachine); return; } goto IL_0155; } awaiter.GetResult(); result = <>4__this.View(); } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); return; } <>1__state = -2; <>t__builder.SetResult(result); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } }
核心代碼剖析:
4. 重點比較一下:F1Async 和 F3Async 的區別
(1).F1Async和F3Async都是異步方法,在外層Index方法中調用的時候,都要加await,就外層而言都不會卡主線程,外層方法都會被編譯成狀態機。
(2).從編譯的角度而言 F1Async 方法本身不會被編譯成狀態機,F3Async方法本身會被編譯成狀態機。
5. 再次擴展
(1).等待的時候要用 await xxxAsync, 而不要用 xxxAsync.wait() 和 .Result
(2).等待多個用 await Task.WhenAll 而不要用 Task.WaitAll
原因?
后者是同步寫法啊,阻塞線程的,從上面的編譯的源碼可以看出來,沒有await不會生成TaskAwaiter<int> awaiter。
二. 幾個用法
1. 異常捕獲
代碼1
public static async void EmailAsync() { List<string> addrs = new List<string>(); IEnumerable<Task> asyncOps = addrs.Select(addr => SendMailAsync(addr)); try { await Task.WhenAll(asyncOps); } catch (AggregateException ex) { // 可以通過 InnerExceptions 來得到內部返回的異常 var exceptions = ex.InnerExceptions; // 也可以使用 Handle 對每個異常進行處理 ex.Handle(innerEx => { // 此處的演示僅僅為了說明 ex.Handle 可以對異常進行單獨處理 // 實際項目中不一定會拋出此異常 if (innerEx is OperationCanceledException oce) { // 對 OperationCanceledException 進行單獨的處理 return true; } else if (innerEx is UnauthorizedAccessException uae) { // 對 UnauthorizedAccessException 進行單獨處理 return true; } return false; }); } }
代碼2
public static async void EmailAsync() { List<string> addrs = new List<string>(); IEnumerable<Task> asyncOps = addrs.Select(addr => SendMailAsync(addr)); try { await Task.WhenAll(asyncOps); } catch (AggregateException ex) { // 此處可以針對每個任務進行更加具體的管理 foreach (Task<string> task in asyncOps) { if (task.IsCanceled) { }else if (task.IsFaulted) { }else if (task.IsCompleted) { } } } }
代碼3
try { HttpClient hc = new HttpClient(); var task1 = hc.GetStringAsync(textBox1.Text); var task2 = hc.GetStringAsync(textBox2.Text); var task3 = hc.GetStringAsync(textBox3.Text); Task.WaitAll(task1, task2, task3); label1.Text = task1.Result.Length.ToString(); label2.Text = task2.Result.Length.ToString(); label3.Text = task3.Result.Length.ToString(); } catch (AggregateException ae) { MessageBox.Show(ae.GetBaseException().ToString()); }
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。