Async和Await异步编程的原理
1. 簡介?
從4.0版本開始.NET引入并行編程庫,用戶能夠通過這個(gè)庫快捷的開發(fā)并行計(jì)算和并行任務(wù)處理的程序。在4.5版本中.NET又引入了Async和Await兩個(gè)新的關(guān)鍵字,在語言層面對(duì)并行編程給予進(jìn)一步的支持,使得用戶能以一種簡潔直觀的方式實(shí)現(xiàn)并行編程。因?yàn)樵诤芏辔臋n里針對(duì)Async和Await這兩個(gè)關(guān)鍵字的使用都被稱為異步編程,為了更符合大眾的閱讀習(xí)慣,我們使用異步編程這個(gè)叫法,意思上和并行編程完全一樣。
關(guān)于Async和Await異步編程的功能說明和使用介紹,MSDN上有詳細(xì)文檔,鏈接如下:
http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx
其它地方也可以搜索到很多相關(guān)文章,這里就不再贅述,本文主要介紹的是異步編程是如何現(xiàn)實(shí)的,背后的原理是什么。
注意:在您閱讀下面內(nèi)容之前請(qǐng)確保已經(jīng)熟悉了異步編程的基本方法。
2. .NET中提供新功能的幾種方法
在繼續(xù)之前總結(jié)一下.NET中提供新功能的三種方法:基于運(yùn)行時(shí)、基于編譯器和基于類庫。
2.1 基于運(yùn)行時(shí)的實(shí)現(xiàn)
顯而易見.NET中大多數(shù)功能都是基于運(yùn)行時(shí)實(shí)現(xiàn)的。比如的類定義的語法、方法的調(diào)用的語法以及所有基本編程語法都有對(duì)應(yīng)的IL代碼,這也正是定義運(yùn)行時(shí)的內(nèi)容之一。所以能編譯為對(duì)應(yīng)專有IL代碼的功能必然是基于運(yùn)行時(shí)實(shí)現(xiàn)的。
2.2 基于編譯器
基于編譯器的實(shí)現(xiàn),最常見的例子就是上下文using和yield。上下文using在VB.NET里干脆就沒有對(duì)應(yīng)的語法,C#編譯器替你做了你在老版本的C#中或VB.NET里要做的工作,就是寫try、finally和Dispose語句。提供基于編譯器的新功能微軟不需要修改運(yùn)行時(shí)。
2.3 基于類庫
這個(gè)不需要太多解釋,所有的編程語言都是通過庫為開發(fā)者提供強(qiáng)大的開發(fā)功能的,庫的豐富程度最終決定一個(gè)語言的發(fā)展前景。
.NET現(xiàn)在常用的運(yùn)行時(shí)只有2.0和4.0兩個(gè)版本,3.0 和3.5都是2.0的運(yùn)行時(shí);4.5的運(yùn)行時(shí)是4.0,它是在編譯器功能和類庫上對(duì)4.0的擴(kuò)展。
3. Async和Await的實(shí)現(xiàn)
前面提到了yield關(guān)鍵字,用于簡化遍歷的實(shí)現(xiàn)。如果您熟悉yield這個(gè)關(guān)鍵字的應(yīng)用,就會(huì)發(fā)現(xiàn)await關(guān)鍵字的出現(xiàn)位置、使用方式以及運(yùn)行邏輯和yield是如此的相似。事實(shí)的確如此,await和async也是一種基于編譯器的功能(C#和VB.NET都提供了這個(gè)功能),不僅如此,它在實(shí)現(xiàn)原理上也和yield非常像——await/async和yield都被編譯器在編譯時(shí)轉(zhuǎn)化為了狀態(tài)機(jī)。
狀態(tài)機(jī)是一種非常常用的編程模式,基本上所有的編譯器都是基于狀態(tài)機(jī)實(shí)現(xiàn)的,當(dāng)訪問這篇博文的時(shí)候?yàn)g覽器就是使用狀態(tài)機(jī)將從cnblogs.com服務(wù)器上獲取的html文本解析為html元素樹,再繪制到屏幕上。
如何發(fā)現(xiàn)或者證實(shí)這一點(diǎn)呢,那就是用.NET的反編譯器,每當(dāng)出現(xiàn)新語法,但凡好奇者都喜歡用反編譯器看一下生成的IL代碼究竟是什么樣子。在Reflector被收購收費(fèi)后(引來吐槽無數(shù)),就一直使用JustDecompile(Telerik在Reflector收費(fèi)后立即推出的免費(fèi)程序),使用JustDecompile時(shí),需要在該程序的Settings中將Show compiler generated types and members選中。也可以用.NET SDK自帶的ILDASM來反編譯,功能雖然最強(qiáng)大,但是只能反編譯為IL匯編語言,用起來有些不便。
首先,下載MSDN上的示例Async Sample Example from Asynchronous Programming with Async and Await,這是一個(gè)簡單的WPF應(yīng)用,用于演示Async/Await異步編程,主要代碼如下:
1 public partial class MainWindow : Window2 {3 // Mark the event handler with async so you can use await in it.4 private async void StartButton_Click(object sender, RoutedEventArgs e)5 {6 // Call and await separately.7 //Task<int> getLengthTask = AccessTheWebAsync();8 You can do independent work here.9 //int contentLength = await getLengthTask; 10 int contentLength = await AccessTheWebAsync(); 11 resultsTextBox.Text += 12 String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength); 13 } 14 15 // Three things to note in the signature: 16 // - The method has an async modifier. 17 // - The return type is Task or Task<T>. (See "Return Types" p.) 18 // Here, it is Task<int> because the return statement returns an integer. 19 // - The method name ends in "Async." 20 async Task<int> AccessTheWebAsync() 21 { 22 // You need to add a reference to System.Net.Http to declare client. 23 HttpClient client = new HttpClient(); 24 25 // GetStringAsync returns a Task<string>. That means that when you await the 26 // task you'll get a string (urlContents). 27 Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); 28 29 // You can do work here that doesn't rely on the string from GetStringAsync. 30 DoIndependentWork(); 31 32 // The await operator suspends AccessTheWebAsync. 33 // - AccessTheWebAsync can't continue until getStringTask is complete. 34 // - Meanwhile, control returns to the caller of AccessTheWebAsync. 35 // - Control resumes here when getStringTask is complete. 36 // - The await operator then retrieves the string result from getStringTask. 37 string urlContents = await getStringTask; 38 39 // The return statement specifies an integer result. 40 // Any methods that are awaiting AccessTheWebAsync retrieve the length value. 41 return urlContents.Length; 42 } 43 44 void DoIndependentWork() 45 { 46 resultsTextBox.Text += "Working . . . . . . .\r\n"; 47 } 48 }?然后,用JustDecompile打開生成的AsyncFirstExample.exe。類視圖如下:
這時(shí)可以看到,MainWindow類中多出了兩個(gè)名稱以u(píng)003c開頭的類,這兩個(gè)類就是狀態(tài)機(jī)類,代碼中有兩個(gè)async函數(shù),因此生成了兩個(gè)狀態(tài)機(jī)類。
因?yàn)榫幾g器轉(zhuǎn)換每個(gè)async函數(shù)的方式都一樣,所以下面的內(nèi)容中都以AccessTheWebAsync這個(gè)函數(shù)為例來說明,該函數(shù)對(duì)應(yīng)的狀態(tài)機(jī)類為u003cAccessTheWebAsyncu003ed__4,反編譯后的C#代碼如下:
1 [CompilerGenerated]2 // <AccessTheWebAsync>d__43 private struct u003cAccessTheWebAsyncu003ed__4 : IAsyncStateMachine4 {5 // <>1__state6 public int u003cu003e1__state;7 8 // <>t__builder9 public AsyncTaskMethodBuilder<int> u003cu003et__builder; 10 11 // <>4__this 12 public MainWindow u003cu003e4__this; 13 14 // <client>5__5 15 public HttpClient u003cclientu003e5__5; 16 17 // <getStringTask>5__6 18 public Task<string> u003cgetStringTasku003e5__6; 19 20 // <urlContents>5__7 21 public string u003curlContentsu003e5__7; 22 23 // <>u__$awaiter8 24 private TaskAwaiter<string> u003cu003eu__u0024awaiter8; 25 26 // <>t__stack 27 private object u003cu003et__stack; 28 29 void MoveNext() 30 { 31 int <>t__result = 0; 32 TaskAwaiter<string> u003cu003eu_u0024awaiter8; 33 try 34 { 35 bool <>t__doFinallyBodies = true; 36 int u003cu003e1_state = this.u003cu003e1__state; 37 if (u003cu003e1_state != -3) 38 { 39 if (u003cu003e1_state == 0) 40 { 41 u003cu003eu_u0024awaiter8 = this.u003cu003eu__u0024awaiter8; 42 TaskAwaiter<string> taskAwaiter = new TaskAwaiter<string>(); 43 this.u003cu003eu__u0024awaiter8 = taskAwaiter; 44 this.u003cu003e1__state = -1; 45 } 46 else 47 { 48 this.u003cclientu003e5__5 = new HttpClient(); 49 this.u003cgetStringTasku003e5__6 = this.u003cclientu003e5__5.GetStringAsync("http://msdn.microsoft.com"); 50 this.u003cu003e4__this.DoIndependentWork(); 51 u003cu003eu_u0024awaiter8 = this.u003cgetStringTasku003e5__6.GetAwaiter(); 52 if (!u003cu003eu_u0024awaiter8.IsCompleted) 53 { 54 this.u003cu003e1__state = 0; 55 this.u003cu003eu__u0024awaiter8 = u003cu003eu_u0024awaiter8; 56 this.u003cu003et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, MainWindow.u003cAccessTheWebAsyncu003ed__4>(ref u003cu003eu_u0024awaiter8, this); 57 <>t__doFinallyBodies = false; 58 return; 59 } 60 } 61 string result = u003cu003eu_u0024awaiter8.GetResult(); 62 u003cu003eu_u0024awaiter8 = new TaskAwaiter<string>(); 63 this.u003curlContentsu003e5__7 = result; 64 <>t__result = this.u003curlContentsu003e5__7.Length; 65 } 66 } 67 catch (Exception exception) 68 { 69 Exception <>t__ex = exception; 70 this.u003cu003e1__state = -2; 71 this.u003cu003et__builder.SetException(<>t__ex); 72 return; 73 } 74 this.u003cu003e1__state = -2; 75 this.u003cu003et__builder.SetResult(<>t__result); 76 } 77 78 [DebuggerHidden] 79 void SetStateMachine(IAsyncStateMachine param0) 80 { 81 this.u003cu003et__builder.SetStateMachine(param0); 82 } 83 }關(guān)于這個(gè)類的命名,C#編譯器命名編譯器生成的類和類成員的方式是:<生成來源名稱>__后綴或輔助說明信息。尖括號(hào)在絕大多數(shù)語言中都是運(yùn)算符,不能用作程序中標(biāo)識(shí)符的命名,但在IL中,標(biāo)識(shí)符都以字符串的形式保存在元數(shù)據(jù)中,通過映射的數(shù)字(一般是元數(shù)據(jù)內(nèi)的本地偏移地址)來表示標(biāo)識(shí)符,因此對(duì)標(biāo)識(shí)符的命名基本沒有限制。C#編譯器利用這一點(diǎn),在編譯器生成的IL代碼中通過使用<和>來明確區(qū)分用戶寫的代碼和編譯器自動(dòng)生成的代碼。
因?yàn)?lt;和>不能用在C#的標(biāo)識(shí)符命名中,反編譯程序JustDecompile對(duì)此做出了處理,將<轉(zhuǎn)換為u003c,>轉(zhuǎn)換為u003e,也就是Unicode編碼。這樣反編譯出來的程序就能直接拷貝到C#編輯器中使用,但是這個(gè)版本的JustDecompile存在一個(gè)bug,就是局部變量中的<和>并沒有被正確的轉(zhuǎn)換為u003c和u003e,所以生成的代碼還是不能直接拷貝就用的,當(dāng)然這并不影響解讀這段代碼。
類u003cAccessTheWebAsyncu003ed__4實(shí)現(xiàn)了接口IAsyncStateMachine,從名字可以看出,這個(gè)接口就是為異步編程定義的。這個(gè)接口只有兩個(gè)方法MoveNext和SetStateMachine,一個(gè)典型的狀態(tài)機(jī)定義:執(zhí)行下一步和設(shè)置狀態(tài)。用一個(gè)簡單的例子快速梳理一下狀態(tài)機(jī)的工作過程,以幫助理解異步編程的機(jī)制:
一個(gè)有1和2兩個(gè)有效狀態(tài)的狀態(tài)機(jī),如果狀態(tài)值為1,調(diào)用MoveNext時(shí)狀態(tài)機(jī)會(huì)執(zhí)行操作A同時(shí)將狀態(tài)值改為2;如果狀態(tài)值為2,調(diào)用MoveNext時(shí)狀態(tài)機(jī)會(huì)執(zhí)行操作B同時(shí)將狀態(tài)值改為3;如果狀態(tài)值為3,調(diào)用MoveNext時(shí)狀態(tài)機(jī)不執(zhí)行任何操作或拋出異常。
在上面的這個(gè)簡單狀態(tài)機(jī)中,調(diào)用者不需要知道狀態(tài)機(jī)下一步要干什么,它只被告知在某個(gè)時(shí)候需要調(diào)用MoveNext,具體干什么由狀態(tài)機(jī)的內(nèi)部實(shí)現(xiàn)決定,異步編程就是利用的這種模式,通過編譯器對(duì)代碼進(jìn)行重組,將一個(gè)await調(diào)用前和調(diào)用后執(zhí)行的代碼分配到狀態(tài)機(jī)的兩個(gè)狀態(tài)中去執(zhí)行。如果一個(gè)async函數(shù)中有兩個(gè)await調(diào)用,那么生成的狀態(tài)機(jī)就會(huì)有3個(gè)狀態(tài),以此類推。如果有循環(huán),根據(jù)循環(huán)的位置不同,狀態(tài)機(jī)狀態(tài)轉(zhuǎn)換更復(fù)雜一些。
回過頭來看異步編程中的異步。在學(xué)習(xí)使用async/await的時(shí)候,很多文檔包括msdn都刻意提到async/await關(guān)鍵字不會(huì)創(chuàng)建新的線程,用async關(guān)鍵字寫的函數(shù)中的代碼都在調(diào)用線程中執(zhí)行。這里是最容易混淆的地方,嚴(yán)格意義上這個(gè)說法不準(zhǔn)確,異步編程必然是多線程的。msdn文檔里提到的不會(huì)創(chuàng)建新線程應(yīng)該是指async函數(shù)本身不會(huì)直接在新線程中運(yùn)行。本質(zhì)上是await調(diào)用的異步函數(shù)執(zhí)行完成后回調(diào)狀態(tài)機(jī)的MoveNext來執(zhí)行余下未執(zhí)行完成的代碼,await調(diào)用的異步函數(shù)必然在某個(gè)地方——也許是嵌套了很深的一個(gè)地方——啟動(dòng)了一個(gè)新的工作線程來完成導(dǎo)致我們要使用異步調(diào)用的耗時(shí)比較長的工作,比如網(wǎng)絡(luò)內(nèi)容讀取。
再看u003cAccessTheWebAsyncu003ed__4類的代碼,u003cu003e1__state這個(gè)成員變量很明顯就是狀態(tài)值了,在48行到50行,當(dāng)狀態(tài)只不等于-3也不等于0的時(shí)候,運(yùn)行的正好是原始C#代碼中await語句前面的代碼,第52行if (!u003cu003eu_u0024awaiter2.IsCompleted)這里很關(guān)鍵,這里正好是異步執(zhí)行最明顯的體現(xiàn),那就是當(dāng)主線程里DoIndependentWork()運(yùn)行結(jié)束的時(shí)候,另一個(gè)線程里獲取http://msdn.microsoft.com頁面內(nèi)容的工作的也可能已經(jīng)完成了。如果獲取頁面的工作完成了,就可以直接運(yùn)行下一狀態(tài)要運(yùn)行的代碼(62行到64行,原始C#代碼中await語句后面的代),而不需要進(jìn)入等待;如果獲取頁面的工作還沒有完成,執(zhí)行第54到58行代碼,將當(dāng)前狀態(tài)機(jī)與TaskAwaiter綁定,同時(shí)將狀態(tài)機(jī)的狀態(tài)值改為0,當(dāng)異步函數(shù)在另一個(gè)線程中執(zhí)行完成時(shí),TaskAwaiter回調(diào)狀態(tài)機(jī)的MoveNext函數(shù),這時(shí)狀態(tài)機(jī)的狀態(tài)為0,運(yùn)行62到64行代碼,完成AcessTheWebAsync函數(shù)的工作。
可見AcessTheWebAsync函數(shù)中原有的代碼都被編譯器重組到狀態(tài)機(jī)中了,那么AcessTheWebAsync函數(shù)現(xiàn)在干什么?可以猜想到的就是創(chuàng)建狀態(tài)機(jī)實(shí)例,設(shè)置初始狀態(tài)(不等于-3也不等于0)和啟動(dòng)狀態(tài)機(jī)。究竟是不是這樣,來看AcessTheWebAsync反編譯出來的C#代碼:
1 private async Task<int> AccessTheWebAsync()2 {3 HttpClient httpClient = new HttpClient();4 Task<string> stringAsync = httpClient.GetStringAsync("http://msdn.microsoft.com");5 this.DoIndependentWork();6 string str = await stringAsync;7 string str1 = str;8 int length = str1.Length;9 return length; 10 }似乎函數(shù)AcessTheWebAsync的代碼和原始的代碼一樣,編譯器并沒有做修改,真的是這樣嗎?答案是否定的,原因是JustDecompile這個(gè)反編譯器太強(qiáng)大了,它竟然將C#編譯器轉(zhuǎn)換的代碼重新還原成async/await語法的代碼了。所以這里我們只能看IL代碼了,切換到IL代碼,可以看到AcessTheWebAsync編譯后的最終的代碼如下:
1 .method private hidebysig instance class [mscorlib]System.Threading.Tasks.Task`1<int32> AccessTheWebAsync () cil managed 2 {3 .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = (4 01 00 00 005 )6 .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = (7 01 00 34 41 73 79 6e 63 46 69 72 73 74 45 78 618 6d 70 6c 65 2e 4d 61 69 6e 57 69 6e 64 6f 77 2b9 3c 41 63 63 65 73 73 54 68 65 57 65 62 41 73 79 10 6e 63 3e 64 5f 5f 34 00 00 11 ) 12 .locals init ( 13 [0] valuetype AsyncFirstExample.MainWindow/'<AccessTheWebAsync>d__4' V_0, 14 [1] class [mscorlib]System.Threading.Tasks.Task`1<int32> V_1, 15 [2] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> V_2 16 ) 17 18 IL_0000: ldloca.s V_0 19 IL_0002: ldarg.0 20 IL_0003: stfld class AsyncFirstExample.MainWindow AsyncFirstExample.MainWindow/'<AccessTheWebAsync>d__4'::'<>4__this' 21 IL_0008: ldloca.s V_0 22 IL_000a: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create() 23 IL_000f: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> AsyncFirstExample.MainWindow/'<AccessTheWebAsync>d__4'::'<>t__builder' 24 IL_0014: ldloca.s V_0 25 IL_0016: ldc.i4.m1 26 IL_0017: stfld int32 AsyncFirstExample.MainWindow/'<AccessTheWebAsync>d__4'::'<>1__state' 27 IL_001c: ldloca.s V_0 28 IL_001e: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> AsyncFirstExample.MainWindow/'<AccessTheWebAsync>d__4'::'<>t__builder' 29 IL_0023: stloc.2 30 IL_0024: ldloca.s V_2 31 IL_0026: ldloca.s V_0 32 IL_0028: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<valuetype AsyncFirstExample.MainWindow/'<AccessTheWebAsync>d__4'>(!!0&) 33 IL_002d: ldloca.s V_0 34 IL_002f: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> AsyncFirstExample.MainWindow/'<AccessTheWebAsync>d__4'::'<>t__builder' 35 IL_0034: call instance class [mscorlib]System.Threading.Tasks.Task`1<int32> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task() 36 IL_0039: stloc.1 37 IL_003a: br.s IL_003c 38 39 IL_003c: ldloc.1 40 IL_003d: ret 41 }仔細(xì)看這段IL匯編代碼,與原始的C#版的AcessTheWebAsync函數(shù)相比幾乎沒有任何相似之處,只有函數(shù)的聲明相同,這就是編譯器轉(zhuǎn)換的結(jié)果。人工將這段IL匯編代碼反編譯成C#:
1 [System.Diagnostics.DebuggerStepThrough()]2 [System.Runtime.CompilerServices.AsyncStateMachine(typeof(u003cAccessTheWebAsyncu003ed__4))]3 private Task<int> AccessTheWebAsync()4 {5 u003cAccessTheWebAsyncu003ed__4 V_0;6 Task<int> V_1;7 System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> V_2;8 9 V_0.u003cu003e4__this = this; 10 V_0.u003cu003et__builder = System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.Create(); 11 V_0.u003cu003e1__state = -1; 12 V_2 = V_0.u003cu003et__builder; 13 V_2.Start(ref V_0); 14 V_1 = V_2.Task; 15 return V_1; 16 }到這里已經(jīng)非常清楚了:AcessTheWebAsync函數(shù)首先創(chuàng)建狀態(tài)機(jī)的實(shí)例,因?yàn)闋顟B(tài)機(jī)類是Struct類型,不需要new;然后,設(shè)置相關(guān)屬性,狀態(tài)機(jī)的初始狀態(tài)值被設(shè)置為-1,符合之前期望的范圍;最后,啟動(dòng)狀態(tài)機(jī),Start方法內(nèi)部會(huì)調(diào)用一次MoveNext,運(yùn)行結(jié)束后返回Task。
多個(gè)async函數(shù)之間的調(diào)用,就是多個(gè)狀態(tài)機(jī)的組合運(yùn)行。
4. 創(chuàng)建一個(gè)真正異步的異步函數(shù)
前面提到await語句await到最后必然調(diào)用了一個(gè)啟動(dòng)了新線程的完成實(shí)際工作的真正異步的異步函數(shù),那么如何自己定義一個(gè)這樣的函數(shù)呢?其實(shí)很簡單,使用System.Threading.Tasks.Task類就可以創(chuàng)建這樣一個(gè)函數(shù),示例代碼如下:
private async void Button_Click(object sender, RoutedEventArgs e){resultsTextBox.Text += String.Format("\r\nMyAsync({0}).\r\n",Thread.CurrentThread.ManagedThreadId); while (true)resultsTextBox.Text += String.Format("\r\nMyAsync({0}): {1}.\r\n", Thread.CurrentThread.ManagedThreadId, await MyAsync());}public Task<string> MyAsync(){var t = new Task<string>((str) =>{var dt = DateTime.Now;Thread.Sleep(4000);return String.Format("({0}){1} - {2}", Thread.CurrentThread.ManagedThreadId, dt, DateTime.Now);}, null);t.Start();return t;}運(yùn)行結(jié)果如下:
這個(gè)程序是在上述msdn提供的示例的基礎(chǔ)上,向界面中加了一個(gè)ID為Button的按鈕,它的事件處理函數(shù)為Button_Click,MyAsync就是我們要?jiǎng)?chuàng)建的函數(shù)。
在這個(gè)真正異步的函數(shù)里卻看不到Aysnc和Await的影子。由此可見,Aysnc和Await是用來組織異步函數(shù)的調(diào)用的,實(shí)現(xiàn)異步代碼和同步代碼間的無縫交互。
5. 結(jié)論?
在.NET 4.5中引入的Async和Await兩個(gè)新的關(guān)鍵字后,用戶能以一種簡潔直觀的方式實(shí)現(xiàn)異步編程。甚至都不需要改變代碼的邏輯結(jié)構(gòu),就能將原來的同步函數(shù)改造為異步函數(shù)。
在內(nèi)部實(shí)現(xiàn)上,Async和Await這兩個(gè)關(guān)鍵字由編譯器轉(zhuǎn)換為狀態(tài)機(jī),通過System.Threading.Tasks中的并行類實(shí)現(xiàn)代碼的異步執(zhí)行。
?
異步編程中的最佳做法
https://docs.microsoft.com/zh-cn/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
總結(jié)
以上是生活随笔為你收集整理的Async和Await异步编程的原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在.NET Core中为gRPC服务
- 下一篇: 一次Redis client组件性能分析