今天就跟大家聊聊有關(guān)如何理解C# 5.0的新特性Async和Await,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
創(chuàng)新互聯(lián)是一家專業(yè)從事成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)的品牌網(wǎng)絡(luò)公司。如今是成都地區(qū)具影響力的網(wǎng)站設(shè)計(jì)公司,作為專業(yè)的成都網(wǎng)站建設(shè)公司,創(chuàng)新互聯(lián)依托強(qiáng)大的技術(shù)實(shí)力、以及多年的網(wǎng)站運(yùn)營(yíng)經(jīng)驗(yàn),為您提供專業(yè)的成都網(wǎng)站建設(shè)、營(yíng)銷型網(wǎng)站建設(shè)及網(wǎng)站設(shè)計(jì)開發(fā)服務(wù)!
一、引言
在之前的C#基礎(chǔ)知識(shí)系列文章中只介紹了從C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,對(duì)于C#又有了新特性的增加——就是C#5.0中async和await兩個(gè)關(guān)鍵字,這兩個(gè)關(guān)鍵字簡(jiǎn)化了異步編程,之所以簡(jiǎn)化了,還是因?yàn)榫幾g器給我們做了更多的工作,下面就具體看看編譯器到底在背后幫我們做了哪些復(fù)雜的工作的。
二、同步代碼存在的問題
對(duì)于同步的代碼,大家肯定都不陌生,因?yàn)槲覀兤匠懙拇a大部分都是同步的,然而同步代碼卻存在一個(gè)很嚴(yán)重的問題,例如我們向一個(gè)Web 三、傳統(tǒng)的異步編程來改善程序的響應(yīng) 上面部分我們已經(jīng)看到同步方法所帶來的實(shí)際問題了,為了解決類似的問題,.NET Framework很早就提供了對(duì)異步編程的支持,下面就用.NET 1.0中提出的異步編程模型(APM)來解決上面的問題,具體代碼如下(注釋的部分通過獲得GUI線程的同步上文對(duì)象,然后同步調(diào)用同步上下文對(duì)象的post方法把要調(diào)用的方法交給GUI線程去處理,因?yàn)榭丶緛砭褪怯蒅UI線程創(chuàng)建的,然后由它自己執(zhí)行訪問控件的操作就不存在跨線程的問題了,程序中使用的是調(diào)用RichTextBox控件的Invoke方式來異步回調(diào)訪問控件的方法,其實(shí)背后的原來和注釋部分是一樣的,調(diào)用RichTextBox控件的Invoke方法可以獲得創(chuàng)建RichTextBox控件的線程信息(也就是前一種方式的同步上下文),然后讓Invoke回調(diào)的方法在該線程上運(yùn)行): 運(yùn)行的結(jié)果為: 四、C# 5.0 提供的async和await使異步編程更簡(jiǎn)單 上面部分演示了使用傳統(tǒng)的異步編程模型(APM)來解決同步代碼所存在的問題,然而在.NET 2.0,.NET 4.0和.NET 4.5中,微軟都有推出新的方式來解決同步代碼的問題,他們分別為基于事件的異步模式,基于任務(wù)的異步模式和提供async和await關(guān)鍵字來對(duì)異步編程支持。關(guān)于前兩種異步編程模式,在我前面的文章中都有介紹,大家可以查看相關(guān)文章進(jìn)行詳細(xì)地了解,本部分就C# 5.0中的async和await這兩個(gè)關(guān)鍵字如何實(shí)現(xiàn)異步編程的問題來給大家介紹下。下面通過代碼來了解下如何使用async和await關(guān)鍵字來實(shí)現(xiàn)異步編程,并且大家也可以參看前面的博客來對(duì)比理解使用async和await是異步編程更簡(jiǎn)單。 運(yùn)行結(jié)果如下: 五、async和await關(guān)鍵字剖析 我們對(duì)比下上面使用async和await關(guān)鍵字來實(shí)現(xiàn)異步編程的代碼和在第二部分的同步代碼,有沒有發(fā)現(xiàn)使用async和await關(guān)鍵字的異步實(shí)現(xiàn)和同步代碼的實(shí)現(xiàn)很像,只是異步實(shí)現(xiàn)中多了async和await關(guān)鍵字和調(diào)用的方法都多了async后綴而已。正是因?yàn)樗麄兊膶?shí)現(xiàn)很像,所以我在第四部分才命名為使用 async和await使異步編程更簡(jiǎn)單,就像我們?cè)趯懲酱a一樣,并且代碼的coding思路也是和同步代碼一樣,這樣就避免考慮在APM中委托的回調(diào)等復(fù)雜的問題,以及在EAP中考慮各種事件的定義。從代碼部分我們可以看出async和await的使用確實(shí)很簡(jiǎn)單,我們就如在寫同步代碼一般,但是我很想知道編譯器到底給我們做了怎樣的處理的?并且從運(yùn)行結(jié)果可以發(fā)現(xiàn),運(yùn)行異步方法的線程和GUI線程的ID是一樣的,也就是說異步方法的運(yùn)行在GUI線程上,所以就不用像APM中那樣考慮跨線程訪問的問題了(因?yàn)橥ㄟ^委托的BeginInvoke方法來進(jìn)行回調(diào)方法時(shí),回調(diào)方法是在線程池線程上執(zhí)行的)。下面就用反射工具看看編譯器把我們的源碼編譯成什么樣子的: 對(duì)于按鈕點(diǎn)擊事件的代碼來說,編譯器生成的背后代碼卻是下面這樣的,完全和我們?cè)创a中的兩個(gè)樣: 看到上面的代碼,作為程序員的我想說——編譯器你怎么可以這樣呢?怎么可以任意篡改我的代碼呢?這樣不是侵犯我的版權(quán)了嗎?你要改最起碼應(yīng)該告訴我一聲吧,如果我的源碼看到它在編譯器中的實(shí)現(xiàn)是上面那樣的,我相信我的源碼會(huì)說——難道我中了世間上最惡毒的面目全非腳嗎? 好吧,為了讓大家更好地理清編譯器背后到底做了什么事情,下面就順著上面的代碼摸瓜,我也來展示耍一套還我漂漂拳來幫助大家找到編譯器代碼和源碼的對(duì)應(yīng)關(guān)系。我的分析思路為: 1、提出問題——我的click事件的源碼到哪里去了呢?從編譯器代碼我們可以看到,前面的7句代碼都是對(duì)某個(gè)類進(jìn)行賦值的操作,最真正起作用的就是最后Start方法的調(diào)用。這里又產(chǎn)生了幾個(gè)疑問—— 如果你看過我的迭代器專題的話,相信你肯定可以聯(lián)想到該結(jié)構(gòu)體就是一個(gè)迭代器的一個(gè)實(shí)現(xiàn),其主要方法就是MoveNext方法。從上面的代碼的注釋應(yīng)該可以幫助我們解決在第一步提到的第一個(gè)問題,即 下面就分析下第二個(gè)問題,從 相信大家從上面的解釋中可以找到源碼與編譯器代碼之間的對(duì)應(yīng)關(guān)系了吧, 但是我在分析完上面的之后,又有一個(gè)疑問——當(dāng)任務(wù)完成時(shí),是如何退出MoveNext方法的呢?總不能讓其一直回調(diào)吧,從上面的代碼的注釋可以看出,當(dāng)任務(wù)執(zhí)行完成之后,會(huì)把<>1__state設(shè)置為0,當(dāng)下次再回調(diào)MoveNext方法時(shí)就會(huì)直接退出方法,然而任務(wù)沒完成之前,同樣也會(huì)把<>1__state設(shè)置為0,但是Switch部分后面的代碼又把<>1__state設(shè)置為-1,這樣就保證了在任務(wù)沒完成之前,MoveNext方法可以被重復(fù)回調(diào),當(dāng)任務(wù)完成之后,<>1__state設(shè)置為-1的代碼將不會(huì)執(zhí)行,而是調(diào)轉(zhuǎn)到 Label_007A部分。 經(jīng)過上面的分析之后,相信大家也可以耍出一套還我漂漂拳去分析異步方法AccessWebAsync(),其分析思路是和btnClick_Click的分析思路是一樣的.這里就不重復(fù)啰嗦了。 分析完之后,下面再分享下幾個(gè)關(guān)于async和await常問的問題 問題一:是不是寫了async關(guān)鍵字的方法就代表該方法是異步方法,不會(huì)堵塞線程呢? 答: 不是的,對(duì)于只標(biāo)識(shí)async關(guān)鍵字的(指在方法內(nèi)沒有出現(xiàn)await關(guān)鍵字)的方法,調(diào)用線程會(huì)把該方法當(dāng)成同步方法一樣執(zhí)行,所以然而會(huì)堵塞GUI線程,只有當(dāng)async和await光劍子同時(shí)出現(xiàn),該方法才被轉(zhuǎn)換為異步方法處理。 問題二:“async”關(guān)鍵字會(huì)導(dǎo)致調(diào)用方法用線程池線程運(yùn)行嗎? 答: 不會(huì),被async關(guān)鍵字標(biāo)識(shí)的方法不會(huì)影響方法是同步還是異步運(yùn)行并完成,而是,它使方法可被分割成多個(gè)片段,其中一些片段可能異步運(yùn)行,這樣這個(gè)方法可能異步完成。這些片段界限就出現(xiàn)在方法內(nèi)部顯示使用”await”關(guān)鍵字的位置處。所以,如果在標(biāo)記了”async”的方法中沒有顯示使用”await”,那么該方法只有一個(gè)片段,并且將以同步方式運(yùn)行并完成。在await關(guān)鍵字出現(xiàn)的前面部分代碼和后面部分代碼都是同步執(zhí)行的(即在調(diào)用線程上執(zhí)行的,也就是GUI線程,所以不存在跨線程訪問控件的問題),await關(guān)鍵處的代碼片段是在線程池線程上執(zhí)行??偨Y(jié)為——使用async和 await關(guān)鍵字實(shí)現(xiàn)的異步方法,此時(shí)的異步方法被分成了多個(gè)代碼片段去執(zhí)行的,而不是像之前的異步編程模型(APM)和EAP那樣,使用線程池線程去執(zhí)行一整個(gè)方法。 看完上述內(nèi)容,你們對(duì)如何理解C# 5.0的新特性Async和Await有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。private void btnClick_Click(object sender, EventArgs e) { this.richTextBox1.Clear(); btnClick.Enabled = false; AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod); IAsyncResult result = caller.BeginInvoke(GetResult, null); //// 捕捉調(diào)用線程的同步上下文派生對(duì)象 //sc= SynchronizationContext.Current; } # region 使用APM實(shí)現(xiàn)異步編程 // 同步方法 private string TestMethod() { // 模擬做一些耗時(shí)的操作 // 實(shí)際項(xiàng)目中可能是讀取一個(gè)大文件或者從遠(yuǎn)程服務(wù)器中獲取數(shù)據(jù)等。 for (int i = 0; i < 10; i++) { Thread.Sleep(200); } return "點(diǎn)擊我按鈕事件完成"; } // 回調(diào)方法 private void GetResult(IAsyncResult result) { AsyncMethodCaller caller = (AsyncMethodCaller)((AsyncResult)result).AsyncDelegate; // 調(diào)用EndInvoke去等待異步調(diào)用完成并且獲得返回值 // 如果異步調(diào)用尚未完成,則 EndInvoke 會(huì)一直阻止調(diào)用線程,直到異步調(diào)用完成 string resultvalue = caller.EndInvoke(result); //sc.Post(ShowState,resultvalue); richTextBox1.Invoke(showStateCallback, resultvalue); } // 顯示結(jié)果到richTextBox private void ShowState(object result) { richTextBox1.Text = result.ToString(); btnClick.Enabled = true; } // 顯示結(jié)果到richTextBox //private void ShowState(string result) //{ // richTextBox1.Text = result; // btnClick.Enabled = true; //} #endregion
private async void btnClick_Click(object sender, EventArgs e) { long length = await AccessWebAsync(); // 這里可以做一些不依賴回復(fù)的操作 OtherWork(); this.richTextBox1.Text += String.Format("\n 回復(fù)的字節(jié)長(zhǎng)度為: {0}.\r\n", length); txbMainThreadID.Text = Thread.CurrentThread.ManagedThreadId.ToString(); } // 使用C# 5.0中提供的async 和await關(guān)鍵字來定義異步方法 // 從代碼中可以看出C#5.0 中定義異步方法就像定義同步方法一樣簡(jiǎn)單。 // 使用async 和await定義異步方法不會(huì)創(chuàng)建新線程, // 它運(yùn)行在現(xiàn)有線程上執(zhí)行多個(gè)任務(wù). // 此時(shí)不知道大家有沒有一個(gè)疑問的?在現(xiàn)有線程上(即UI線程上)運(yùn)行一個(gè)耗時(shí)的操作時(shí), // 為什么不會(huì)堵塞UI線程的呢? // 這個(gè)問題的答案就是 當(dāng)編譯器看到await關(guān)鍵字時(shí),線程會(huì) private async Task
// 編譯器為按鈕Click事件生成的代碼 private void btnClick_Click(object sender, EventArgs e) {
//
當(dāng)前文章:如何理解C#5.0的新特性Async和Await
瀏覽地址:http://weahome.cn/article/gpseeg.html