C#中async await異步關(guān)鍵字用法和異步的底層原理全解析
C#異步編程
一、異步編程基礎(chǔ)
異步編程是啥玩意兒
- 就是讓程序在干等著某些耗時(shí)操作(比如等網(wǎng)絡(luò)響應(yīng)、讀寫文件啥的)的時(shí)候,能把線程騰出來干別的活兒,這樣程序就能更靈敏、更高效啦。
- 跟同步編程不一樣,同步編程就是老老實(shí)實(shí)等著操作完成才繼續(xù)往下走,線程就一直被占著,多浪費(fèi)啊。
異步編程的好處
- 響應(yīng)快:比如在做UI界面的時(shí)候,用了異步編程,界面就不會(huì)卡啦,用戶體驗(yàn)賊棒。
- 省資源:不用讓線程一直干等著,資源利用率就上去了。
- 能扛更多活兒:面對(duì)一大堆并發(fā)操作的時(shí)候,異步編程能輕松搞定,擴(kuò)展性杠杠滴。
二、異步方法的工作原理
異步方法咋被編譯的
- 你寫個(gè)
async
修飾的方法,編譯器就把它變成一個(gè)狀態(tài)機(jī)啦。 - 狀態(tài)機(jī)會(huì)根據(jù)
await
表達(dá)式把方法拆成好多個(gè)狀態(tài),就跟玩拼圖一樣。
狀態(tài)機(jī)是咋干活的
- 狀態(tài)機(jī)就是編譯器生成的一個(gè)類,它得記著異步方法執(zhí)行到哪兒了。
- 核心就是
MoveNext
方法,它就像導(dǎo)演一樣,指揮著異步操作一步步往下走。 - 每碰到一個(gè)
await
,就切換一下狀態(tài)。
await
底層是咋實(shí)現(xiàn)的
await
就整出個(gè)等待器(awaiter),專門等著異步操作完成。- 要是操作還沒完,
await
就記下當(dāng)前狀態(tài),等操作完了再繼續(xù)往下走。
三、代碼示例
用HttpClient
干異步網(wǎng)絡(luò)請(qǐng)求
- 弄個(gè)
HttpClient
對(duì)象,用來發(fā)HTTP請(qǐng)求。 - 用
GetStringAsync
方法,就能異步拿到指定URL的網(wǎng)頁(yè)內(nèi)容啦。 - 把拿到的內(nèi)容打印出來,瞧一瞧成果。
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
方法,就能把文件內(nèi)容異步讀出來。 - 把讀到的內(nèi)容打印出來,看看對(duì)不對(duì)。
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("文件內(nèi)容:" + s); } } }
四、編譯后的底層實(shí)現(xiàn)
用ILSpy反編譯DLL文件
- ILSpy就是個(gè)反編譯工具,能把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("文件內(nèi)容:" + 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() { // This method is implemented by the compiler-generated code. } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { this.<>t__builder.SetStateMachine(stateMachine); } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { this.SetStateMachine(stateMachine); } }
看看編譯后的狀態(tài)機(jī)代碼
- 分析狀態(tài)機(jī)類的結(jié)構(gòu),看看都有啥變量、
MoveNext
方法長(zhǎng)啥樣。 - 瞧瞧
awaiter
咋用的,狀態(tài)咋切換的。
理解MoveNext
方法是干啥的
MoveNext
就是狀態(tài)機(jī)的發(fā)動(dòng)機(jī),它決定了異步方法咋執(zhí)行。- 在這個(gè)方法里,會(huì)根據(jù)當(dāng)前狀態(tài)執(zhí)行對(duì)應(yīng)的代碼,碰到
await
就暫停,安排好后續(xù)咋繼續(xù)。
五、總結(jié)
異步方法編譯過程回顧
- 再嘮嘮
async
方法咋被編譯成狀態(tài)機(jī)的,狀態(tài)機(jī)又咋根據(jù)await
拆分方法、驅(qū)動(dòng)異步操作的。
await
到底在干啥
- 說白了,
await
根本不是真的“等待”,而是靠狀態(tài)機(jī)和等待器來實(shí)現(xiàn)的異步協(xié)作。 - 強(qiáng)調(diào)一下異步編程的好處,比如響應(yīng)快、省資源、能扛更多活兒,還有啥場(chǎng)景適合用它。
到此這篇關(guān)于C#中async await異步關(guān)鍵字用法和異步的底層原理的文章就介紹到這了,更多相關(guān)C# async await異步關(guān)鍵字內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#結(jié)合JavaScript實(shí)現(xiàn)上傳視頻到騰訊云點(diǎn)播平臺(tái)的操作方法
這篇文章主要介紹了C#結(jié)合JavaScript實(shí)現(xiàn)上傳視頻到騰訊云點(diǎn)播平臺(tái),上傳視頻功能,主要要解決兩個(gè)問題,一是在服務(wù)端通過C#生成簽名和SDKID,二是在客戶端通過JavaScript上傳視頻到騰訊云點(diǎn)播服務(wù)器,感興趣的朋友跟隨小編一起看看吧2023-11-11Unity中協(xié)程IEnumerator的使用方法介紹詳解
本文主要介紹了Unity中協(xié)程IEnumerator的使用方法介紹詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06C#實(shí)現(xiàn)簡(jiǎn)易計(jì)算器功能(2)(窗體應(yīng)用)
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)簡(jiǎn)易計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01Unity游戲開發(fā)實(shí)現(xiàn)背包系統(tǒng)的示例詳解
這篇文章主要為大家介紹了Unity游戲開發(fā)實(shí)現(xiàn)背包系統(tǒng)的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08C# 如何實(shí)現(xiàn)一個(gè)帶通知的List<T>
這篇文章主要介紹了C# 如何實(shí)現(xiàn)一個(gè)帶通知的List<T>,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-02-02