C# 5.0新特性:Async和Await使異步編程更簡(jiǎn)單_第1頁(yè)
C# 5.0新特性:Async和Await使異步編程更簡(jiǎn)單_第2頁(yè)
C# 5.0新特性:Async和Await使異步編程更簡(jiǎn)單_第3頁(yè)
C# 5.0新特性:Async和Await使異步編程更簡(jiǎn)單_第4頁(yè)
C# 5.0新特性:Async和Await使異步編程更簡(jiǎn)單_第5頁(yè)
已閱讀5頁(yè),還剩6頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、在之前的C#基礎(chǔ)知識(shí)系列文章中只介紹了從C#1.0到C#4.0中主要的特性,然而.NET4.5的推出,對(duì)于C#又有了新特性的增加就是C#5.0中async和await兩個(gè)關(guān)鍵字,這兩個(gè)關(guān)鍵字簡(jiǎn)化了異步編程,之所以簡(jiǎn)化了,還是因?yàn)榫幾g器給我們做了更多的工作,下面就具體看看編譯器到底在背后幫我們做了哪些復(fù)雜的工作的。AD:2013大數(shù)據(jù)全球技術(shù)峰會(huì)課程PPT下載一、引言在之前的C#基礎(chǔ)知識(shí)系列文章中只介紹了從C#1.0到C#4.0中主要的特性,然而.NET4.5的推出,對(duì)于C#又有了新特性的增加就是C#5.0中async和await兩個(gè)關(guān)鍵字,這兩個(gè)關(guān)鍵字簡(jiǎn)化了異步編程,之所以簡(jiǎn)化了,還是因?yàn)榫幾g

2、器給我們做了更多的工作,下面就具體看看編譯器到底在背后幫我們做了哪些復(fù)雜的工作的。二、同步代碼存在的問(wèn)題對(duì)于同步的代碼,大家肯定都不陌生,因?yàn)槲覀兤匠?xiě)的代碼大部分都是同步的,然而同步代碼卻存在一個(gè)很?chē)?yán)重的問(wèn)題,例如我們向一個(gè)Web服務(wù)器發(fā)出一個(gè)請(qǐng)求時(shí),如果我們發(fā)出請(qǐng)求的代碼是同步實(shí)現(xiàn)的話,這時(shí)候我們的應(yīng)用程序就會(huì)處于等待狀態(tài),直到收回一個(gè)響應(yīng)信息為止,然而在這個(gè)等待的狀態(tài),對(duì)于用戶(hù)不能操作任何的UI界面以及也沒(méi)有任何的消息,如果我們?cè)噲D去操作界面時(shí),此時(shí)我們就會(huì)看到應(yīng)用程序?yàn)轫憫?yīng)的信息(在應(yīng)用程序的窗口旁),相信大家在平常使用桌面軟件或者訪問(wèn)web的時(shí)候,肯定都遇到過(guò)這樣類(lèi)似的情況的,對(duì)于這

3、個(gè),大家肯定會(huì)覺(jué)得看上去非常不舒服。引起這個(gè)原因正是因?yàn)榇a的實(shí)現(xiàn)是同步實(shí)現(xiàn)的,所以在沒(méi)有得到一個(gè)響應(yīng)消息之前,界面就成了一個(gè)卡死狀態(tài)了,所以這對(duì)于用戶(hù)來(lái)說(shuō)肯定是不可接受的,因?yàn)槿绻乙獜姆?wù)器上下載一個(gè)很大的文件時(shí),此時(shí)我們甚至不能對(duì)窗體進(jìn)行關(guān)閉的操作的。為了具體說(shuō)明同步代碼存在的問(wèn)題(造成界面開(kāi)始),下面通過(guò)一個(gè)程序讓大家更形象地看下問(wèn)題所在:/單擊事件privatevoidbtnClick_Click(objectsender,EventArgse)this.btnClick.Enabled=false;longlength=AccessWeb();this.btnClick.Enabl

4、ed=true;/這里可以做一些不依賴(lài)回復(fù)的操作OtherWork();this.richTextBox1.Text+=String.Format(n回復(fù)的字節(jié)長(zhǎng)度為:length);txbMainThreadID.Text=Thread.CurrentThread.ManagedThreadId.ToString();privatelongAccessWeb()MemoryStreamcontent=newMemoryStream();0.rn,/對(duì)MSDN發(fā)起一個(gè)Web請(qǐng)求HttpWebRequestwebRequestWebRequest.Create(if(webRequest!=nu

5、ll)/返回回復(fù)結(jié)果using(WebResponseresponse=webRequest.GetResponse()using(StreamresponseStream=response.GetResponseStream()responseStream.CopyTo(content);txbAsynMethodID.Text=Thread.CurrentThread.ManagedThreadId.ToString();returncontent.Length;=運(yùn)行程序后,當(dāng)我們點(diǎn)擊窗體的點(diǎn)擊我按鈕之后,在得到服務(wù)器響應(yīng)之前,我們不能對(duì)窗體進(jìn)行任何的操作,包括移動(dòng)窗體,關(guān)閉窗體等,具體

6、運(yùn)行結(jié)果如下:三、傳統(tǒng)的異步編程來(lái)改善程序的響應(yīng)上面部分我們已經(jīng)看到同步方法所帶來(lái)的實(shí)際問(wèn)題了,為了解決類(lèi)似的問(wèn)題,.NETFramework很早就提供了對(duì)異步編程的支持,下面就用.NET1.0中提出的異步編程模型(APM)來(lái)解決上面的問(wèn)題,http:/www.321321.cc/具體代碼如下(注釋的部分通過(guò)獲得GUI線程的同步上文對(duì)象,然后同步調(diào)用同步上下文對(duì)象的post方法把要調(diào)用的方法交給GUI線程去處理,因?yàn)榭丶緛?lái)就是由GUI線程創(chuàng)建的,然后由它自己執(zhí)行訪問(wèn)控件的操作就不存在跨線程的問(wèn)題了,程序中使用的是調(diào)用RichTextBox控件的Invoke方式來(lái)異步回調(diào)訪問(wèn)控件的方法,其實(shí)背

7、后的原來(lái)和注釋部分是一樣的,調(diào)用RichTextBox控件的Invoke方法可以獲得創(chuàng)建RichTextBox控件的線程信息(也就是前一種方式的同步上下文),然后讓Invoke回調(diào)的方法在該線程上運(yùn)行):privatevoidbtnClick_Click(objectsender,EventArgse)this.richTextBox1.Clear();btnClick.Enabled=false;AsyncMethodCallercaller=newAsyncMethodCaller(TestMethod);IAsyncResultresult=caller.BeginInvoke(GetR

8、esult,null);/捕捉調(diào)用線程的同步上下文派生對(duì)象/sc=SynchronizationContext.Current;#region使用APM實(shí)現(xiàn)異步編程/同步方法privatestringTestMethod()/模擬做一些耗時(shí)的操作/實(shí)際項(xiàng)目中可能是讀取一個(gè)大文件或者從遠(yuǎn)程服務(wù)器中獲取數(shù)據(jù)等。for(inti=0;i10;i+)Thread.Sleep(200);return點(diǎn)擊我按鈕事件完成;/回調(diào)方法privatevoidGetResult(IAsyncResultresult)AsyncMethodCallercaller=(AsyncMethodCaller)(Async

9、Result)result).AsyncDelegate;/調(diào)用EndInvoke去等待異步調(diào)用完成并且獲得返回值/如果異步調(diào)用尚未完成,則EndInvoke會(huì)一直阻止調(diào)用線程,直到異步調(diào)用完成stringresultvalue=caller.EndInvoke(result);/sc.Post(ShowState,resultvalue);richTextBox1.Invoke(showStateCallback,resultvalue);/顯示結(jié)果到richTextBoxprivatevoidShowState(objectresult)richTextBox1.Text=result.T

10、oString();btnClick.Enabled=true;/顯示結(jié)果到richTextBox/privatevoidShowState(stringresult)/richTextBox1.Text=result;/btnClick.Enabled=true;/#endregion運(yùn)行的結(jié)果為:上面部分演示了使用傳統(tǒng)的異步編程模型(APM)來(lái)解決同步代碼所存在的問(wèn)題,然而在.NET2.0,.NET4.0和.NET4.5中,微軟都有推出新的方式來(lái)解決同步代碼的問(wèn)題,他們分別為基于事件的異步模式,基于任務(wù)的異步模式和提供async和await關(guān)鍵字來(lái)對(duì)異步編程支持。關(guān)于前兩種異步編程模式,在

11、我前面的文章中都有介紹,大家可以查看相關(guān)文章進(jìn)行詳細(xì)地了解,本部分就C#5.0中的async和await這兩個(gè)關(guān)鍵字如何實(shí)現(xiàn)異步編程的問(wèn)題來(lái)給大家介紹下。下面通過(guò)代碼來(lái)了解下如何使用async和await關(guān)鍵字來(lái)實(shí)現(xiàn)異步編程,并且大家也可以參看前面的博客來(lái)對(duì)比理解使用async和await是異步編程更簡(jiǎn)單。privateasyncvoidbtnClick_Click(objectsender,EventArgse)longlength=awaitAccessWebAsync();/這里可以做一些不依賴(lài)回復(fù)的操作OtherWork();this.richTextBox1.Text+=String

12、.Format(n回復(fù)的字節(jié)長(zhǎng)度為:length);txbMainThreadID.Text=Thread.CurrentThread.ManagedThreadId.ToString();/從代碼中可以看出C#5.0中定義異步方法就像定義同步方法一樣簡(jiǎn)單。/使用async和await定義異步方法不會(huì)創(chuàng)建新線程,/它運(yùn)行在現(xiàn)有線程上執(zhí)行多個(gè)任務(wù)./此時(shí)不知道大家有沒(méi)有一個(gè)疑問(wèn)的?在現(xiàn)有線程上(即UI線程上)運(yùn)行一個(gè)耗時(shí)的操作時(shí),/為什么不會(huì)堵塞UI線程的呢?/這個(gè)問(wèn)題的答案就是當(dāng)編譯器看到await關(guān)鍵字時(shí),線程會(huì)privateasyncTaskAccessWebAsync()MemorySt

13、reamcontent=newMemoryStream();/對(duì)MSDN發(fā)起一個(gè)Web請(qǐng)求HttpWebRequestwebRequestWebRequest.Create(if(webRequest!=null)/返回回復(fù)結(jié)果using(WebResponseresponse=awaitwebRequest.GetResponseAsync()0.rn,=using(StreamresponseStream=response.GetResponseStream()awaitresponseStream.CopyToAsync(content);txbAsynMethodID.Text=Thr

14、ead.CurrentThread.ManagedThreadId.ToString();returncontent.Length;privatevoidOtherWork()this.richTextBox1.Text+=rn等待服務(wù)器回復(fù)中.n;運(yùn)行結(jié)果如下:五、async和await關(guān)鍵字剖析我們對(duì)比下上面使用async和await關(guān)鍵字來(lái)實(shí)現(xiàn)異步編程的代碼和在第二部分的同步代碼,有沒(méi)有發(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)很像,所以我在第四部分才命名為使用

15、async和await使異步編程更簡(jiǎn)單,就像我們?cè)趯?xiě)同步代碼一樣,并且代碼的coding思路也是和同步代碼一樣,這樣就避免考慮在APM中委托的回調(diào)等復(fù)雜的問(wèn)題,以及在EAP中考慮各種事件的定義。從代碼部分我們可以看出async和await的使用確實(shí)很簡(jiǎn)單,我們就如在寫(xiě)同步代碼一般,但是我很想知道編譯器到底給我們做了怎樣的處理的?并且從運(yùn)行結(jié)果可以發(fā)現(xiàn),運(yùn)行異步方法的線程和GUI線程的ID是一樣的,也就是說(shuō)異步方法的運(yùn)行在GUI線程上,所以就不用像APM中那樣考慮跨線程訪問(wèn)的問(wèn)題了(因?yàn)橥ㄟ^(guò)委托的BeginInvoke方法來(lái)進(jìn)行回調(diào)方法時(shí),回調(diào)方法是在線程池線程上執(zhí)行的)。下面就用反射工具看看編

16、譯器把我們的源碼編譯成什么樣子的:對(duì)于按鈕點(diǎn)擊事件的代碼來(lái)說(shuō),編譯器生成的背后代碼卻是下面這樣的,完全和我們?cè)创a中的兩個(gè)樣:/編譯器為按鈕Click事件生成的代碼privatevoidbtnClick_Click(objectsender,EventArgse)d_0d_;d_.4_this=this;d_.sender=sender;d_.e=e;d_.t_builder=AsyncVoidMethodBuilder.Create();d_.1_state=-1;d_.t_builder.Startd_0(refd_);看到上面的代碼,作為程序員的我想說(shuō)編譯器你怎么可以這樣呢?怎么可以任意篡

17、改我的代碼呢?這樣不是侵犯我的版權(quán)了嗎?你要改最起碼應(yīng)該告訴我一聲吧,如果我的源碼看到它在編譯器中的實(shí)現(xiàn)是上面那樣的,我相信我的源碼會(huì)說(shuō)難道我中了世間上最?lèi)憾镜拿婺咳悄_嗎?好吧,為了讓大家更好地理清編譯器背后到底做了什么事情,下面就順著上面的代碼摸瓜,我也來(lái)展示耍一套還我漂漂拳來(lái)幫助大家找到編譯器代碼和源碼的對(duì)應(yīng)關(guān)系。我的分析思路為:1、提出問(wèn)題我的click事件的源碼到哪里去了呢?從編譯器代碼我們可以看到,前面的7句代碼都是對(duì)某個(gè)類(lèi)進(jìn)行賦值的操作,最真正起作用的就是最后Start方法的調(diào)用。這里又產(chǎn)生了幾個(gè)疑問(wèn)d_0是什么類(lèi)型?該類(lèi)型中的t_builder字段類(lèi)型的Start方法到底是做什

18、么用的?有了這兩個(gè)疑問(wèn),我們就點(diǎn)擊d_0(反射工具可以讓我們直接點(diǎn)擊查看)來(lái)看看它是什么類(lèi)型/d_0類(lèi)型的定義,從下面代碼可以看出它是一個(gè)結(jié)構(gòu)體/該類(lèi)型是編譯器生成的一個(gè)嵌入類(lèi)型/看到該類(lèi)型的實(shí)現(xiàn)有沒(méi)有讓你聯(lián)想到什么?privatestructd_0:IAsyncStateMachine/Fieldspublicint1_state;publicForm14_this;publicAsyncVoidMethodBuildert_builder;privateobjectt_stack;privateTaskAwaiteru_$awaiter2;publiclong5_1;publicEvent

19、Argse;publicobjectsender;/MethodsprivatevoidMoveNext()tryTaskAwaiterCS$0$0001;boolt_doFinallyBodies=true;switch(this.1_state)case-3:gotoLabel_010E;case0:break;default:/獲取用于等待Task(任務(wù))的等待者。你要知道某個(gè)任務(wù)是否完成,我們就需要一個(gè)等待者對(duì)象對(duì)該任務(wù)進(jìn)行一個(gè)監(jiān)控,所以微軟就定義了一個(gè)等待者對(duì)象的/從這里可以看出,其實(shí)async和await關(guān)鍵字背后的實(shí)現(xiàn)原理是基于任務(wù)的異步編程模式(TAP)/這里代碼是在線程池線程

20、上運(yùn)行的CS$0$0001=this.4_this.AccessWebAsync().GetAwaiter();/如果任務(wù)完成就調(diào)轉(zhuǎn)到Label_007A部分的代碼if(CS$0$0001.IsCompleted)gotoLabel_007A;/設(shè)置狀態(tài)為0為了退出回調(diào)方法。this.1_state=0;this.u_$awaiter2=CS$0$0001;/這個(gè)代碼是做什么用的呢?讓我們帶著問(wèn)題看下面的分析this.t_builder.AwaitUnsafeOnCompletedTaskAwaiter,Form1.d_0(refCS$0$0001,refthis);t_doFinallyBo

21、dies=false;/返回到調(diào)用線程,即GUI線程,這也是該方法不會(huì)堵塞GUI線程的原因,不管任務(wù)是否完成都返回到GUI線程return;/當(dāng)任務(wù)完成時(shí),不會(huì)執(zhí)行下面的代碼,會(huì)直接執(zhí)行Label_007A中代碼CS$0$0001=this.u_$awaiter2;this.u_$awaiter2=newTaskAwaiter();/為了使再次回調(diào)MoveNext代碼this.1_state=-1;Label_007A:/下面代碼是在GUI線程上執(zhí)行的CS$0$0001=newTaskAwaiter();longCS$0$0003=CS$0$0001.GetResult();this.5_1=

22、CS$0$0003;/我們?cè)创a中的代碼這里的this.4_this.OtherWork();this.4_this.richTextBox1.Text=this.4_this.richTextBox1.Text+string.Format(n回復(fù)的字節(jié)長(zhǎng)度為:0.rn,this.5_1);this.4_this.txbMainThreadID.Text=Thread.CurrentThread.ManagedThreadId.ToString();catch(Exceptiont_ex)this.1_state=-2;this.t_builder.SetException(t_ex);retu

23、rn;Label_010E:this.1_state=-2;this.t_builder.SetResult();DebuggerHiddenprivatevoidSetStateMachine(IAsyncStateMachineparam0)this.t_builder.SetStateMachine(param0);如果你看過(guò)我的迭代器專(zhuān)題的話,相信你肯定可以聯(lián)想到該結(jié)構(gòu)體就是一個(gè)迭代器的一個(gè)實(shí)現(xiàn),其主要方法就是MoveNext方法。從上面的代碼的注釋?xiě)?yīng)該可以幫助我們解決在第一步提到的第一個(gè)問(wèn)題,即d_0是什么類(lèi)型,下面就分析下第二個(gè)問(wèn)題,從d_0結(jié)構(gòu)體的代碼中可以發(fā)現(xiàn)t_builder

24、的類(lèi)型是AsyncVoidMethodBuilder類(lèi)型,下面就看看它的Start方法的解釋運(yùn)行關(guān)聯(lián)狀態(tài)機(jī)的生成器,即調(diào)用該方法就可以開(kāi)始運(yùn)行狀態(tài)機(jī),運(yùn)行狀態(tài)機(jī)指的就是執(zhí)行MoveNext方法(MoveNext方法中有我們?cè)创a中所有代碼,這樣就把編譯器生成的Click方法與我們的源碼關(guān)聯(lián)起來(lái)了)。從上面代碼注釋中可以發(fā)現(xiàn),當(dāng)該MoveNext被調(diào)用時(shí)會(huì)立即還回到GUI線程中,同時(shí)也有這樣的疑問(wèn)剛開(kāi)始調(diào)用MoveNext方法時(shí),任務(wù)肯定是還沒(méi)有被完成的,但是我們輸出我們?cè)创a中的代碼,必須等待任務(wù)完成(因?yàn)槿蝿?wù)完成才能調(diào)轉(zhuǎn)到Label_007A中的代碼),此時(shí)我們應(yīng)該需要回調(diào)MoveNext方法來(lái)

25、檢查任務(wù)是否完成,(就如迭代器中的,我們需要使用foreach語(yǔ)句一直調(diào)用MoveNext方法),然而我們?cè)诖a卻沒(méi)有找到回調(diào)的任何代碼???對(duì)于這個(gè)疑問(wèn),回調(diào)MoveNext方法肯定是存在的,只是首次看上面代碼的朋友還沒(méi)有找到類(lèi)似的語(yǔ)句而已,上面代碼注釋中我提到了一個(gè)問(wèn)題這個(gè)代碼是做什么用的呢?讓我們帶著問(wèn)題看下面的分析,其實(shí)注釋下面的代碼就是起到回調(diào)MoveNext方法的作用,AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted方法就是調(diào)度狀態(tài)機(jī)去執(zhí)行MoveNext方法,從而也就解決了回調(diào)MoveNext的疑問(wèn)了。相信大家從上面的解釋中可以找到源碼與編

26、譯器代碼之間的對(duì)應(yīng)關(guān)系了吧,但是我在分析完上面的之后,又有一個(gè)疑問(wèn)當(dāng)任務(wù)完成時(shí),是如何退出MoveNext方法的呢?總不能讓其一直回調(diào)吧,從上面的代碼的注釋可以看出,當(dāng)任務(wù)執(zhí)行完成之后,會(huì)把1_state設(shè)置為0,當(dāng)下次再回調(diào)MoveNext方法時(shí)就會(huì)直接退出方法,然而任務(wù)沒(méi)完成之前,同樣也會(huì)把1_state設(shè)置為0,但是Switch部分后面的代碼又把1_state設(shè)置為-1,這樣就保證了在任務(wù)沒(méi)完成之前,MoveNext方法可以被重復(fù)回調(diào),當(dāng)任務(wù)完成之后,1_state設(shè)置為-1的代碼將不會(huì)執(zhí)行,而是調(diào)轉(zhuǎn)到Label_007A部分。經(jīng)過(guò)上面的分析之后,相信大家也可以耍出一套還我漂漂拳去分析異步方法AccessWebAsync(),其分析思路是和btnClick_Click的分析思路是一樣的.這里就不重復(fù)啰嗦了。分析完之后,下面再分享下幾個(gè)關(guān)于async和await常問(wèn)的

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論