一、async、await本質
直接說結論:它們是C#提供的語法糖,編譯器編譯後是狀態機的調用。
先看如下的一段代碼,要main方法中調用了三個await方法
將此dll進行反編譯爲4.0的代碼如下:
可見到兩個Main方法,也就是說我們在程序中Main方法上加了async關鍵詞,編譯器會編譯成一個是異步的一個是非異步方法,程序還是將非異步的方法作爲入口函數。進入函數
在該函數中調了異步的Main方法,再進入:
在該函數中創建了一個狀態機,將參數傳給狀態機,並調用期Start方法,可知異步方法實際上是狀態機方法的調用
進入狀態機類型<Main>d__0
private sealed class <Main>d__0 : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder <>t__builder; public string[] args; private string <text>5__1; private HttpClient <httpClient>5__2; private string <html>5__3; private string <>s__4; private string <>s__5; private TaskAwaiter<string> <>u__1; private TaskAwaiter <>u__2; private void MoveNext() { int num = <>1__state; try { TaskAwaiter awaiter2; TaskAwaiter<string> awaiter; switch (num) { default: <httpClient>5__2 = new HttpClient(); goto case 0; case 0: try { TaskAwaiter<string> awaiter3; if (num != 0) { awaiter3 = <httpClient>5__2.GetStringAsync("https://www.baidu.com").GetAwaiter(); if (!awaiter3.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter3; <Main>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter3, ref stateMachine); return; } } else { awaiter3 = <>u__1; <>u__1 = default(TaskAwaiter<string>); num = (<>1__state = -1); } <>s__4 = awaiter3.GetResult(); <html>5__3 = <>s__4; <>s__4 = null; Console.WriteLine(<html>5__3); <html>5__3 = null; } finally { if (num < 0 && <httpClient>5__2 != null) { ((IDisposable)<httpClient>5__2).Dispose(); } } <httpClient>5__2 = null; awaiter2 = File.WriteAllTextAsync("E:\\test.txt", "zhengwei").GetAwaiter(); if (!awaiter2.IsCompleted) { num = (<>1__state = 1); <>u__2 = awaiter2; <Main>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } goto IL_015f; case 1: awaiter2 = <>u__2; <>u__2 = default(TaskAwaiter); num = (<>1__state = -1); goto IL_015f; case 2: { awaiter = <>u__1; <>u__1 = default(TaskAwaiter<string>); num = (<>1__state = -1); break; } IL_015f: awaiter2.GetResult(); Console.WriteLine("寫入成功"); awaiter = File.ReadAllTextAsync("E:\\test.txt").GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 2); <>u__1 = awaiter; <Main>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } break; } <>s__5 = awaiter.GetResult(); <text>5__1 = <>s__5; <>s__5 = null; Console.WriteLine("文件內容" + <text>5__1); } catch (Exception exception) { <>1__state = -2; <text>5__1 = null; <>t__builder.SetException(exception); return; } <>1__state = -2; <text>5__1 = null; <>t__builder.SetResult(); } 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); } }
在此狀態機類中可以看到我們自己寫的Main方法中的主要代碼已編譯到了方法MoveNext()中,並將我們的局部變量編譯成了成員變量
方法中有一個case,我們自己寫的三個異步方法被編譯後拆到了多個case中去了,MoveNext方法會根據不同的num被多次調用
編譯的代碼中可見到在每次調用異步方法時都會去判斷是否完成
編譯後的代碼意思是當執行到string html = await httpClient.GetStringAsync("https://www.baidu.com");時,如果沒有完成就直接返回了,並不會再往下執行
只有當此段代碼執行完成後會繼續往下執行,當執行到第二個異步方法await File.WriteAllTextAsync(@"E:\test.txt","zhengwei")時又會返回,等待執行完後再往下執行
同理,執行第三步異步方法var text = await File.ReadAllTextAsync(@"E:\test.txt");也是如此
所有異步方法都 執行完成後,會break;跳出狀態機。
未完待續。。。
二、async背後的線程切換
await調用的等待期間,.net會把當前的線程返回線線程池,等異步方法調用執行完畢後,框架會從線程池再取出來一個線程執行後續的代碼。
代碼驗證
運行的結果可知在這個方法中使用的並不是同一個進程,一個是1,一個是9
當然,有時寫入的內容少,也有可能方法開始和結束的線程id是一樣的,
三、異步方法與多線程的關係
異步方法的代碼並不會自動在新的線程中執行,除非把代碼放到新線程中執行。
在方法調用前和方法調用中用的是同一個線程,都是1,並沒有開啓一個新的線程
除非把這個異步方法放到一個新的線程中:可以看到方法調用中的線程爲4,調用前的線程爲1。
四、CancellationToken在異步方法中的使用