這篇文章主要介紹“如何理解Await與Async ”,在日常操作中,相信很多人在如何理解Await與Async 問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”如何理解Await與Async ”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
創(chuàng)新互聯(lián)公司 - 達(dá)州服務(wù)器托管,四川服務(wù)器租用,成都服務(wù)器租用,四川網(wǎng)通托管,綿陽(yáng)服務(wù)器托管,德陽(yáng)服務(wù)器托管,遂寧服務(wù)器托管,綿陽(yáng)服務(wù)器托管,四川云主機(jī),成都云主機(jī),西南云主機(jī),達(dá)州服務(wù)器托管,西南服務(wù)器托管,四川/成都大帶寬,成都機(jī)柜租用,四川老牌IDC服務(wù)商
一:背景
1. 講故事
await,async 這玩意的知識(shí)點(diǎn)已經(jīng)被人說(shuō)的爛的不能再爛了,看似沒(méi)什么好說(shuō)的,但我發(fā)現(xiàn)有不少文章還是從理論上講述了這兩個(gè)語(yǔ)法糖的用法,懂得還是懂,不懂的看似懂了過(guò)幾天又不懂了,人生如戲全靠記是不行的哈,其實(shí)本質(zhì)上來(lái)說(shuō) await, async 只是編譯器層面上的語(yǔ)法糖,在 IL 層面都會(huì)被打成原型的,所以在這個(gè)層面上認(rèn)識(shí)這兩個(gè)語(yǔ)法糖是非常有必要的。
二:從 IL 層面認(rèn)識(shí)
1. 使用 WebClient 下載
為了方便打回原型,我先上一個(gè)例子,使用 webclient 異步下載 http://cnblogs.com 的html,代碼如下:
class Program { static void Main(string[] args) { var html = GetResult(); Console.WriteLine("稍等... 正在下載 cnblogs -> html \r\n"); var content = html.Result; Console.WriteLine(content); } static async Task<string> GetResult() { var client = new WebClient(); var content = await client.DownloadStringTaskAsync(new Uri("http://cnblogs.com")); return content; } }
上面的代碼非常簡(jiǎn)單,可以看到異步操作沒(méi)有阻塞主線程輸出: 稍等... 正在下載 cnblogs -> html \r\n, 編譯器層面沒(méi)什么好說(shuō)的 ,接下來(lái)看下在 IL 層面發(fā)生了什么?
2. 挖掘 await async 的IL代碼
還是老規(guī)矩, ilSpy 走起,如下圖:
可以看到,這里有一個(gè) GetResult 方法 ,一個(gè) Main 方法,還有一個(gè)不知道在哪里冒出來(lái)的
<1 style="box-sizing: border-box;"> \d__1> 類(lèi)
因?yàn)椴恢缽哪睦锩俺鰜?lái)的,特別引人關(guān)注,所以看看它的 IL 是咋樣的?
.class nested private auto ansi sealed beforefieldinit '<GetResult>d__1' extends [System.Runtime]System.Object implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine { .method private final hidebysig newslot virtual instance void MoveNext () cil managed { } .method private final hidebysig newslot virtual instance void SetStateMachine ( class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine ) cil managed { } }
從上面的 IL 代碼可以看到,這是自動(dòng)生成的
看到里面的 MoveNext 是不是很眼熟,平時(shí)你在 foreach 集合的時(shí)候就會(huì)用到這個(gè)方法,那時(shí)人家叫做枚舉類(lèi),在這里算是被改造了一下, 叫狀態(tài)機(jī)???。
<2 style="box-sizing: border-box;"> GetResult ()
為了方便演示,我對(duì)方法體中的 IL 代碼做一下簡(jiǎn)化:
.method private hidebysig static class [System.Runtime]System.Threading.Tasks.Task`1<string> GetResult () cil managed { IL_0000: newobj instance void ConsoleApp3.Program/'<GetResult>d__1'::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string>::Create() IL_000c: stfld valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string> ConsoleApp3.Program/'<GetResult>d__1'::'<>t__builder' IL_0011: ldloc.0 IL_0012: ldc.i4.m1 IL_0013: stfld int32 ConsoleApp3.Program/'<GetResult>d__1'::'<>1__state' IL_0018: ldloc.0 IL_0019: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string> ConsoleApp3.Program/'<GetResult>d__1'::'<>t__builder' IL_001e: ldloca.s 0 IL_0020: call instance void valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string>::Start<class ConsoleApp3.Program/'<GetResult>d__1'>(!!0&) IL_0025: ldloc.0 IL_0026: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string> ConsoleApp3.Program/'<GetResult>d__1'::'<>t__builder' IL_002b: call instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string>::get_Task() IL_0030: ret } // end of method Program::GetResult
如果你稍微懂一點(diǎn)的話,在 IL_0000 處的 newobj 你就應(yīng)該知道這個(gè)方法就是做了 new
<3 style="box-sizing: border-box;"> Main
Main方法沒(méi)有做任何改變,原來(lái)是什么樣現(xiàn)在還是什么樣。
三:將 IL 代碼 回寫(xiě)為 C#
1. 完整 C# 代碼
通過(guò)前面一部分你應(yīng)該對(duì) await ,async 在 IL 層面有了一個(gè)框架性的認(rèn)識(shí),這里我就全部反寫(xiě)成 C# 代碼:
class Program { static void Main(string[] args) { var html = GetResult(); Console.WriteLine("稍等... 正在下載 cnblogs -> html \r\n"); var content = html.Result; Console.WriteLine(content); } static Task<string> GetResult() { GetResult stateMachine = new GetResult(); stateMachine.builder = AsyncTaskMethodBuilder<string>.Create(); stateMachine.state = -1; stateMachine.builder.Start(ref stateMachine); return stateMachine.builder.Task; } } class GetResult : IAsyncStateMachine { public int state; public AsyncTaskMethodBuilder<string> builder; private WebClient client; private string content; private string s3; private TaskAwaiter<string> awaiter; public void MoveNext() { var result = string.Empty; TaskAwaiter<string> localAwaiter; GetResult stateMachine; int num = state; try { if (num == 0) { localAwaiter = awaiter; awaiter = default(TaskAwaiter<string>); num = state = -1; } else { client = new WebClient(); localAwaiter = client.DownloadStringTaskAsync(new Uri("http://cnblogs.com")).GetAwaiter(); if (!localAwaiter.IsCompleted) { num = state = 0; awaiter = localAwaiter; stateMachine = this; builder.AwaitUnsafeOnCompleted(ref localAwaiter, ref stateMachine); return; } } s3 = localAwaiter.GetResult(); content = s3; s3 = null; result = content; } catch (Exception exx) { state = -2; client = null; content = null; builder.SetException(exx); } state = -2; client = null; content = null; builder.SetResult(result); } public void SetStateMachine(IAsyncStateMachine stateMachine) { } }
可以看到,回寫(xiě)成 C# 代碼之后跑起來(lái)是沒(méi)有任何問(wèn)題的,為了方便理解,我先來(lái)畫(huà)一張流程圖。
通過(guò)上面的 xmind,它基本流程就是: stateMachine.builder.Start(ref stateMachine) -> GetResult.MoveNext -> client.DownloadStringTaskAsync -> localAwaiter.IsCompleted = false -> builder.AwaitUnsafeOnCompleted(ref localAwaiter, ref stateMachine) -> GetResult.MoveNext -> localAwaiter.GetResult() -> builder.SetResult(result)
2. 剖析 AsyncTaskMethodBuilder
其實(shí)你仔細(xì)觀察會(huì)發(fā)現(xiàn),所謂的 await,async 的異步化運(yùn)作都是由 AsyncTaskMethodBuilder 承載的,如異步任務(wù)的啟動(dòng),對(duì)html結(jié)果的封送,接觸底層IO,其中 Task
然后著重看下 AwaitUnsafeOnCompleted 方法,這個(gè)方法非常重要,其注釋如下:
// // Summary: // Schedules the state machine to proceed to the next action when the specified // awaiter completes. This method can be called from partially trusted code. public void AwaitUnsafeOnCompleted<[NullableAttribute(0)] TAwaiter, [NullableAttribute(0)] TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine;
一旦調(diào)用了這個(gè)方法,就需要等待 底層IO 將任務(wù)處理完畢之后二次回調(diào) GetResult.MoveNext,也就表示要么異常要么完成任務(wù), Awaiter 包裝的 Task 結(jié)果封送到 builder.SetResult。
然后簡(jiǎn)單說(shuō)一下 狀態(tài)機(jī) 的走法,通過(guò)調(diào)試會(huì)發(fā)現(xiàn)這里會(huì)走 兩次 MoveNext,一次啟動(dòng),一次拿結(jié)果。
<1> 第一次回調(diào) MoveNext
第一次 MoveNext 的觸發(fā)由 stateMachine.builder.Start(ref stateMachine) 發(fā)起,可以用 DNSpy 去調(diào)試一下,如下圖:
<2> 第二次回調(diào) MoveNext
第二次 MoveNext 的觸發(fā)由 builder.AwaitUnsafeOnCompleted(ref localAwaiter, ref stateMachine) 開(kāi)始,可以看到一旦 網(wǎng)絡(luò)驅(qū)動(dòng)程序 處理完畢后就由線程池IO線程主動(dòng)發(fā)起到最后觸發(fā)代碼中的 MoveNext,最后就是到 awaiter 中獲取 task 的 result 處結(jié)束,如下圖:
到此,關(guān)于“如何理解Await與Async ”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
文章標(biāo)題:如何理解Await與Async
鏈接地址:http://jinyejixie.com/article42/gggghc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、關(guān)鍵詞優(yōu)化、網(wǎng)站排名、App開(kāi)發(fā)、企業(yè)建站、網(wǎng)站改版
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)