C#使用async和await實(shí)現(xiàn)異步編程_第1頁(yè)
C#使用async和await實(shí)現(xiàn)異步編程_第2頁(yè)
C#使用async和await實(shí)現(xiàn)異步編程_第3頁(yè)
C#使用async和await實(shí)現(xiàn)異步編程_第4頁(yè)
C#使用async和await實(shí)現(xiàn)異步編程_第5頁(yè)
已閱讀5頁(yè),還剩7頁(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)介

第C#使用async和await實(shí)現(xiàn)異步編程最近在寫(xiě)程序的時(shí)候,經(jīng)常遇到大量需要異步訪(fǎng)問(wèn)的情況,但是對(duì)于async和await到底怎么寫(xiě),還不是非常明確。

1.普通的程序怎么寫(xiě)?

classProgram

staticvoidMain(string[]args)

MyDownLoadStringds=newMyDownLoadString();

ds.DoRun();

Console.ReadKey();

classMyDownLoadString

Stopwatchsw=newStopwatch();

publicvoidDoRun()

constintLargeNumber=6000000;

sw.Start();

intt1=CountCharacters(1,"");

intt2=CountCharacters(2,"");

CountToALargeNumber(1,LargeNumber);

CountToALargeNumber(2,LargeNumber);

CountToALargeNumber(3,LargeNumber);

CountToALargeNumber(4,LargeNumber);

Console.WriteLine("CharsinCall1:{0}",t1);

Console.WriteLine("CharsinCall1:{0}",t2);

privateintCountCharacters(intid,stringuriString)

WebClientwc1=newWebClient();

Console.WriteLine("Call{0}start:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

stringresult=wc1.DownloadString(newUri(uriString));

Console.WriteLine("Call{0}completed:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

returnresult.Length;

privatevoidCountToALargeNumber(intid,intvalue)

for(longi=0;ivalue;i++);

Console.WriteLine("EndCountToALargeNumber{0}:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

}

結(jié)果:

Call1start:1ms

Call1completed:903ms

Call2start:903ms

Call2completed:1,355ms

EndCountToALargeNumber1:1,375ms

EndCountToALargeNumber2:1,399ms

EndCountToALargeNumber3:1,417ms

EndCountToALargeNumber4:1,435ms

CharsinCall1:161702

CharsinCall1:5164

從運(yùn)行結(jié)果可以看到,同步執(zhí)行的時(shí)間主要花在了兩次請(qǐng)求外部地址上,計(jì)算長(zhǎng)度并不費(fèi)時(shí),用圖來(lái)表示就像下面

2.使用async和await怎么寫(xiě)?

修改上面代碼,如下

classMyDownLoadString

Stopwatchsw=newStopwatch();

publicvoidDoRun()

constintLargeNumber=6000000;

sw.Start();

//Taskint保存結(jié)果對(duì)象,后面t1.Result則是獲取結(jié)果

Taskintt1=CountCharactersAsync(1,"");

Taskintt2=CountCharactersAsync(2,"");

//無(wú)需等待CountCharactersAsync執(zhí)行完成

CountToALargeNumber(1,LargeNumber);

CountToALargeNumber(2,LargeNumber);

CountToALargeNumber(3,LargeNumber);

CountToALargeNumber(4,LargeNumber);

//t1.Result獲取結(jié)果

Console.WriteLine("CharsinCall1:{0}",t1.Result);

Console.WriteLine("CharsinCall1:{0}",t2.Result);

privateasyncTaskintCountCharactersAsync(intid,stringuriString)

WebClientwc=newWebClient();

Console.WriteLine("Call{0}start:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

stringresult=awaitwc.DownloadStringTaskAsync(newUri(uriString));

Trace.TraceInformation("TaceingAsyncCall{0}@time:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

Console.WriteLine("Call{0}completed:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

returnresult.Length;

privatevoidCountToALargeNumber(intid,intvalue)

for(longi=0;ivalue;i++);

Console.WriteLine("EndCountToALargeNumber{0}:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

}

運(yùn)行結(jié)果:

Call1start:2ms

Call2start:253ms

EndCountToALargeNumber1:288ms

EndCountToALargeNumber2:359ms

EndCountToALargeNumber3:560ms

Call1completed:770ms

EndCountToALargeNumber4:844ms

Call2completed:887ms

CharsinCall1:162262

CharsinCall2:5164

修改如上面的代碼之后,我們就可以無(wú)需等待兩次CountCharactersAsync返回結(jié)果,而是直接調(diào)用了下面的CountToALargeNumber,在CountCharactersAsync請(qǐng)求返回的時(shí)候再獲取結(jié)果。

3.async和await的細(xì)節(jié)

async和await可以創(chuàng)建和使用異步方法,這個(gè)特性的由三個(gè)部分組成:

①調(diào)用方法(callingmethod):該方法調(diào)用異步方法,然后在異步方法(可能使用同一個(gè)線(xiàn)程也可能不在一個(gè)線(xiàn)程)執(zhí)行其任務(wù)的時(shí)候繼續(xù)執(zhí)行②異步方法(async):該方法異步執(zhí)行其工作,然后立即方法到調(diào)用方法③await表達(dá)式:用于異步方法內(nèi)部,指明需要異步執(zhí)行的惹怒我。一個(gè)異步方法可以包含任意多個(gè)await表達(dá)式,如果一個(gè)都不包含編譯器會(huì)發(fā)出警告

舉例說(shuō)明一個(gè)async/await方法:

//1.調(diào)用方法

staticvoidMain(string[]args)

Taskintt=DoSumAsync(1,2);

Console.WriteLine("結(jié)果:{0}",t.Result);

Console.ReadKey();

//2.異步方法

publicstaticasyncTaskintDoSumAsync(inta,intb)

//3.await表達(dá)式

intsum=awaitTask.Run(()={returna+b;});

returnsum;

}

4.什么是異步方法?

上面簡(jiǎn)單舉例了什么是異步方法,下面就詳細(xì)學(xué)習(xí)一下:

異步方法在完成其工作之前返回到調(diào)用方法,并在調(diào)用方法繼續(xù)執(zhí)行的時(shí)候完成其工作。語(yǔ)法上有如下特征:

①方法使用async作為修飾符②方法內(nèi)部包含一個(gè)或者多個(gè)await表達(dá)式,表示可以異步完成的任務(wù)③必須具備以下三種返回類(lèi)型void、Task、TaskT,其中后兩種的返回對(duì)象標(biāo)識(shí)講座未來(lái)完成的工作,調(diào)用方法和異步方法可以繼續(xù)執(zhí)行。④異步方法的參數(shù)可以任意類(lèi)型,但是不能為out和ref參數(shù)⑤約定俗成,一般異步方法都是以Async作為后綴的。⑥除了方法之外,Lambda表達(dá)式和匿名函數(shù)也可以作為異步對(duì)象。

像代碼:

privateasyncTaskintCountCharactersAsync(intid,stringuriString)

WebClientwc=newWebClient();

Console.WriteLine("Call{0}start:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

stringresult=awaitwc.DownloadStringTaskAsync(newUri(uriString));

Trace.TraceInformation("TaceingAsyncCall{0}@time:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

Console.WriteLine("Call{0}completed:{1:N0}ms",id,sw.Elapsed.TotalMilliseconds);

returnresult.Length;

}

詳細(xì)說(shuō)明:

①async關(guān)鍵字是一個(gè)上下文關(guān)鍵字,也就是說(shuō)除了做為方法(lambda和匿名函數(shù))的修飾符之外,還可以做標(biāo)識(shí)符。

②返回類(lèi)型

Task類(lèi)型:如果調(diào)用方法不需要從異步方法中返回某個(gè)值,但需要檢查異步方法的狀態(tài),可以返回一個(gè)Task,此時(shí)就算異步方法中出現(xiàn)了return語(yǔ)句,也不會(huì)返回任何東西。TaskT類(lèi)型,除了上面Task的功能,還可以通過(guò)Return屬性來(lái)獲取返回的T類(lèi)型的值。void類(lèi)型:如果僅僅是執(zhí)行異步方法,而不需要與它做任何進(jìn)一步的交互(調(diào)用并忘記),此時(shí)可以用void,和Task一樣,就算有return語(yǔ)句,也得不到任何東西。

5.異步方法的控制流

首先要明確異步方法的三個(gè)部分,如下圖所示:

①首先是第一個(gè)await之前的部分,這部分應(yīng)該是少量且無(wú)需長(zhǎng)時(shí)間等待的代碼。②await表達(dá)式,表示需要被異步執(zhí)行的任務(wù),這里有兩個(gè)await表達(dá)式,第二個(gè)await和之前的同步部分和第一個(gè)await以及之前的部分是一樣的。③后續(xù)部分:在await表達(dá)式之后出現(xiàn)的方法中的其余代碼。

執(zhí)行過(guò)程,可以參考下面的圖

有幾個(gè)注意的地方:

①await之前的部分是同步執(zhí)行的②當(dāng)達(dá)到awati的時(shí)候,會(huì)將異步方法的控制返回給調(diào)用方法。如果方法返回的類(lèi)型是Task或者TaskT,將創(chuàng)建一個(gè)Task對(duì)象,表示需異步完成的任務(wù)和后續(xù),然后將該Task返回到調(diào)用方法。這里的返回值并不是await表達(dá)式的返回值,而是異步方法中聲明的返回值類(lèi)型。③異步方法內(nèi)部需要完成以下工作:

-異步執(zhí)行await表達(dá)是的空閑任務(wù)

-當(dāng)await表達(dá)式執(zhí)行完成之后,執(zhí)行后續(xù)部分。后續(xù)本身也可能是await表達(dá)式,處理過(guò)程和上一個(gè)一致。

-后續(xù)部分如果遇到return或者方法達(dá)到末尾,將做如下的事情:

l如果返回的類(lèi)型是void,控制流就退出了

l如果返回的類(lèi)型是Task,后續(xù)部分設(shè)置Task對(duì)象的屬性并退出。

l如果返回的類(lèi)型是TaskT,不僅要設(shè)置Task對(duì)象屬性,還要設(shè)置Task對(duì)象的Return屬性。

這個(gè)點(diǎn)要注意下:并不是遇到return或者達(dá)到方法末尾,就能獲取到返回值,它只是退出了。

④調(diào)用方法繼續(xù)執(zhí)行,會(huì)從異步方法獲取Task對(duì)象。當(dāng)需要其實(shí)際值的時(shí)候,就引用Task對(duì)象中的Result屬性。屆時(shí),如果異步方法設(shè)置了該屬性,調(diào)用方法獲取其值并繼續(xù)。否則就等待該屬性被設(shè)置,然后再繼續(xù)執(zhí)行。

6.await表達(dá)式

await表達(dá)式指定了一個(gè)異步執(zhí)行的任務(wù)。語(yǔ)法由await關(guān)鍵字+一個(gè)空閑對(duì)象(稱(chēng)為任務(wù))組成。這個(gè)任務(wù)可能是一個(gè)Task對(duì)象,也可以不是,默認(rèn)情況下由該線(xiàn)程異步執(zhí)行。

一個(gè)空閑對(duì)象指的是一個(gè)awaitable類(lèi)型的實(shí)例,awaitable類(lèi)型是指包含了GetAwaiter方法的類(lèi)型,方法沒(méi)有參數(shù),返回一個(gè)稱(chēng)為awaiter類(lèi)型的對(duì)象。

一個(gè)awaiter對(duì)象包含了如下成員:

一般情況下我們不需要自己構(gòu)建一個(gè)awaiter對(duì)象,使用.net自己的Task就可以了。最簡(jiǎn)單的方法就是使用Task.Run()來(lái)返回一個(gè)Task對(duì)象。關(guān)于Task.Run()有一個(gè)非常重要的點(diǎn),他將在不同的線(xiàn)程上運(yùn)行你的方法。

6.異常處理和await表達(dá)式

先看下面這個(gè)例子,直接在異步方法內(nèi)部使用了try..catch。

staticvoidMain(string[]args)

Taskt=BadAsync();

t.Wait();

Console.WriteLine("TaskStatus:{0}",t.Status);

Console.WriteLine("TaskIsFaulted:{0}",t.IsFaulted);

Console.WriteLine("Pleaseenterakeytoexit!");

Console.ReadKey();

staticasyncTaskBadAsync()

awaitTask.Run(()={thrownewException();});

catch

Console.WriteLine("ExceptioninBadAsync");

}

執(zhí)行結(jié)果:

ExceptioninBadAsync

TaskStatus:RanToCompletion

TaskIsFaulted:False

Pleaseenterakeytoexit!

從結(jié)果可以看到,雖然在異步方法內(nèi)部進(jìn)行了try..catch,并且也catch到了異常,但是對(duì)于調(diào)用函數(shù),返回的Task狀態(tài)依然為RanToCompletion。

為什么這個(gè)亞子?,原因如下:

①Task沒(méi)有被取消掉②沒(méi)有未處理的異常。類(lèi)似的IsFaulted是false。

7.在調(diào)用方法中同步的等待任務(wù)(WaitAll、WaitAny)

對(duì)于單個(gè)Task,可以通過(guò)task對(duì)象的wait()方法來(lái)進(jìn)行等待。

Taskintt=CountCharactersAsync("");

t.Wait();

對(duì)于多個(gè)Task,可以使用WaitAll()或者waitAny()方法,進(jìn)行同步。

WaitAll是等待所以的任務(wù)完成才繼續(xù)操作

Taskintt1=CountCharactersAsync(1,"");

Taskintt2=CountCharactersAsync(2,"");

Taskint[]tasks=newTaskint[]{t1,t2};

Task.WaitAll(tasks);

WaitAny是只要一個(gè)完成就可以繼續(xù)操作

Taskintt1=CountCharactersAsync(1,"");

Taskintt2=CountCharactersAsync(2,"");

Taskint[]tasks=newTaskint[]{t1,t2};

Task.WaitAny(tasks);

8.在異步方法中異步的等待任務(wù)(WhenAll、.WhenAny)

上面說(shuō)明了如何在調(diào)用方法中,同步等待Task的完成。但是有時(shí)候,我們?cè)谝粋€(gè)異步方法中也會(huì)存在多個(gè)任務(wù),想要讓它們通過(guò)await表達(dá)式等待。我們可以通過(guò)Task.WhenAll()和Task.WhenAny()方法實(shí)現(xiàn)。這兩個(gè)方法稱(chēng)為組合子(combinator)。

privateasyncTaskintCountCharactersAsync(stringsite1,stringsite2)

WebClientwc1=newWebClient();

WebClientwc2=newWebClient();

Taskstringt1=wc1.DownloadStringTaskAsync(newUri(site1));

Taskstringt2=wc2.DownloadStringTaskAsync(newUri(site2));

ListTaskstringtasks=newListTaskstring();

tasks.Add(t1);

tasks

溫馨提示

  • 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)論