目錄 C#異步編程 一、異步編程基礎 二、異步方法的工作原理 三、代碼示例 四、編譯后的底層實現 五、總結
C#異步編程
一、異步編程基礎
異步編程是啥玩意兒 就是讓程序在干等著某些耗時操作(比如等網絡響應、讀寫文件啥的)的時候,能把線程騰出來干別的活兒,這樣程序就能更靈敏、更高效啦。 跟同步編程不一樣,同步編程就是老老實實等著操作完成才繼續往下走,線程就一直被占著,多浪費啊。 異步編程的好處 響應快 :比如在做UI界面的時候,用了異步編程,界面就不會卡啦,用戶體驗賊棒。省資源 :不用讓線程一直干等著,資源利用率就上去了。能扛更多活兒 :面對一大堆并發操作的時候,異步編程能輕松搞定,擴展性杠杠滴。
二、異步方法的工作原理
異步方法咋被編譯的 你寫個async
修飾的方法,編譯器就把它變成一個狀態機啦。 狀態機會根據await
表達式把方法拆成好多個狀態,就跟玩拼圖一樣。 狀態機是咋干活的 狀態機就是編譯器生成的一個類,它得記著異步方法執行到哪兒了。 核心就是MoveNext
方法,它就像導演一樣,指揮著異步操作一步步往下走。 每碰到一個await
,就切換一下狀態。 await
底層是咋實現的 await
就整出個等待器(awaiter),專門等著異步操作完成。要是操作還沒完,await
就記下當前狀態,等操作完了再繼續往下走。
三、代碼示例
用HttpClient
干異步網絡請求 弄個HttpClient
對象,用來發HTTP請求。 用GetStringAsync
方法,就能異步拿到指定URL的網頁內容啦。 把拿到的內容打印出來,瞧一瞧成果。
using System ;
using System. Net. Http ;
using System. Threading. Tasks ; namespace asyncawait原理1
{ class Program { static async Task Main ( string [ ] args) { using ( HttpClient httpClient = new HttpClient ( ) ) { string html = await httpClient. GetStringAsync ( "https://www.baidu.com" ) ; Console. WriteLine ( html) ; } } }
}
異步讀寫文件 用File.WriteAllTextAsync
方法,能把文本異步寫到指定路徑的文件里。 用File.ReadAllTextAsync
方法,就能把文件內容異步讀出來。 把讀到的內容打印出來,看看對不對。
using System ;
using System. IO ;
using System. Threading. Tasks ; namespace asyncawait原理1
{ class Program { static async Task Main ( string [ ] args) { string txt = "hello world" ; string filename = @"E:\temp\1.txt" ; await File. WriteAllTextAsync ( filename, txt) ; Console. WriteLine ( "寫入成功" ) ; string s = await File. ReadAllTextAsync ( filename) ; Console. WriteLine ( "文件內容:" + s) ; } }
}
四、編譯后的底層實現
用ILSpy反編譯DLL文件 ILSpy就是個反編譯工具,能把DLL文件變回C#代碼,方便咱們研究。 把DLL文件加載到ILSpy里,就能看到編譯后的代碼啦。
[ CompilerGenerated ]
private sealed class < > c__DisplayClass0_0 : IAsyncStateMachine
{ public int < > 1__state; public AsyncTaskMethodBuilder < > t__builder; public string [ ] args; private string < > s__1; private string < > s__3; private string < > s__6; private HttpClient < httpClient> __4; private string < html> __5; private string < txt> __2; private string < filename> __7; private void MoveNext ( ) { int num = this . < > 1__state; try { TaskAwaiter< string > awaiter; TaskAwaiter awaiter2; switch ( num) { default : this . < httpClient> __4 = new HttpClient ( ) ; goto case 0 ; case 0 : try { awaiter = this . < httpClient> __4. GetStringAsync ( "https://www.baidu.com" ) . GetAwaiter ( ) ; if ( ! awaiter. IsCompleted) { num = this . < > 1__state = 0 ; this . < > t__builder. AwaitUnsafeOnCompleted ( ref awaiter, ref this ) ; return ; } } catch ( Exception exception) { this . < > 1__state = - 2 ; this . < > t__builder. SetException ( exception) ; return ; } this . < html> __5 = awaiter. GetResult ( ) ; Console. WriteLine ( this . < html> __5) ; this . < txt> __2 = "hello yz" ; this . < filename> __7 = @"E:\temp\1.txt" ; awaiter2 = File. WriteAllTextAsync ( this . < filename> __7, this . < txt> __2) . GetAwaiter ( ) ; if ( ! awaiter2. IsCompleted) { num = this . < > 1__state = 1 ; this . < > t__builder. AwaitUnsafeOnCompleted ( ref awaiter2, ref this ) ; return ; } break ; case 1 : awaiter2 = this . < > s__1; this . < > s__1 = null ; num = this . < > 1__state = - 1 ; break ; } awaiter2. GetResult ( ) ; Console. WriteLine ( "寫入成功" ) ; this . < > s__3 = null ; awaiter = File. ReadAllTextAsync ( this . < filename> __7) . GetAwaiter ( ) ; if ( ! awaiter. IsCompleted) { num = this . < > 1__state = 2 ; this . < > t__builder. AwaitUnsafeOnCompleted ( ref awaiter, ref this ) ; return ; } this . < > s__6 = awaiter. GetResult ( ) ; Console. WriteLine ( "文件內容:" + this . < > s__6) ; this . < > s__6 = null ; this . < > t__builder. SetResult ( ) ; } catch ( Exception exception) { this . < > 1__state = - 2 ; this . < > t__builder. SetException ( exception) ; return ; } this . < > 1__state = - 1 ; } void IAsyncStateMachine. MoveNext ( ) { } [ DebuggerHidden ] private void SetStateMachine ( IAsyncStateMachine stateMachine) { this . < > t__builder. SetStateMachine ( stateMachine) ; } void IAsyncStateMachine. SetStateMachine ( IAsyncStateMachine stateMachine) { this . SetStateMachine ( stateMachine) ; }
}
看看編譯后的狀態機代碼 分析狀態機類的結構,看看都有啥變量、MoveNext
方法長啥樣。 瞧瞧awaiter
咋用的,狀態咋切換的。 理解MoveNext
方法是干啥的 MoveNext
就是狀態機的發動機,它決定了異步方法咋執行。在這個方法里,會根據當前狀態執行對應的代碼,碰到await
就暫停,安排好后續咋繼續。
五、總結
異步方法編譯過程回顧 再嘮嘮async
方法咋被編譯成狀態機的,狀態機又咋根據await
拆分方法、驅動異步操作的。 await
到底在干啥 說白了,await
根本不是真的“等待”,而是靠狀態機和等待器來實現的異步協作。 強調一下異步編程的好處,比如響應快、省資源、能扛更多活兒,還有啥場景適合用它。