溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C#中怎么實(shí)現(xiàn)異步編程

發(fā)布時(shí)間:2021-07-07 15:17:43 來(lái)源:億速云 閱讀:413 作者:Leah 欄目:大數(shù)據(jù)

這篇文章給大家介紹C#中怎么實(shí)現(xiàn)異步編程,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

異步方法基礎(chǔ)及其運(yùn)行流程

Async和Await

異步方法使用async修飾,該方法包含一個(gè)或多個(gè)await表達(dá)式或語(yǔ)句,方法同步運(yùn)行,直至到達(dá)第一個(gè) Await,此時(shí)暫停,直到等待的任務(wù)完成,在任務(wù)完成后,控制權(quán)返回給方法的調(diào)用方。如果方法中并不包含await,則該方法不會(huì)像同步方法一樣被掛起。

異步方法通常包含await運(yùn)算符的一個(gè)或多個(gè)實(shí)例,但缺少await表達(dá)式也不會(huì)導(dǎo)致生成編譯器錯(cuò)誤,之會(huì)因?yàn)闆](méi)有await而發(fā)出警告,但編譯依然通過(guò)。

異步方法使用await關(guān)鍵字來(lái)確定等待位置,但await表達(dá)式并不阻止正在執(zhí)行到此位置的線程,也就是說(shuō)異步方法在await表達(dá)式執(zhí)行時(shí)只是暫停,并不會(huì)導(dǎo)致方法退出,只會(huì)導(dǎo)致finally代碼塊不運(yùn)行。異步方法只有在等待的任務(wù)完成后,才能通過(guò)該位置并繼續(xù)執(zhí)行剩下的邏輯,控制權(quán)也在此處返回給異步方法的調(diào)用方。

如果異步方法未使用Await運(yùn)算符標(biāo)記暫停點(diǎn),那么異步方法會(huì)作為同步方法執(zhí)行,即使有Async修飾符,也不例外。如以下示例

   1:  public async static Task<string> GetUserInfoAsync()
   2:  {
   3:      User user = await db.User.FirstOrDefaultAsync();//此處會(huì)掛起
   4:
   5:      Task<User> user = db.User.FirstOrDefaultAsync();//此處不會(huì)掛起,注意此處,返回值也變了,接下來(lái)會(huì)討論一下異步方法的返回值
   6:
   7:      return string.Empty;
   8:  }
 

具M(jìn)SDN描述,aysnc關(guān)鍵字是一個(gè)非保留的關(guān)鍵字。在修飾方法或 lambda 表達(dá)式時(shí),它是關(guān)鍵字,await也作為關(guān)鍵字存在。在所有其他上下文中,async和await都會(huì)將其解釋為標(biāo)識(shí)符。不過(guò)開(kāi)發(fā)人員可以不用太過(guò)關(guān)注這段,只需要知道aysnc會(huì)將一個(gè)方法標(biāo)識(shí)成異步方法,而await可以掛起異步方法的執(zhí)行即可。


 

關(guān)鍵點(diǎn)

1、和被async修飾的方法不一樣,如果方法中含有await關(guān)鍵字,方法必須使用async標(biāo)識(shí)符,否則編譯不通過(guò)。

2、在異步編程過(guò)程中,比較推薦的做法是,被標(biāo)記了async關(guān)鍵字的異步方法應(yīng)該包含至少一個(gè)await表達(dá)式或語(yǔ)句。

3、異步方法的命名以Async結(jié)尾 

異步返回類型和異常處理

需要說(shuō)明的是,本文所討論的異步方法指的是基于任務(wù)的異步編程模型,返回值是,Task或Task<TResult>。

1、如果方法需要返回string類型,那么將返回Task<string>。如果方法沒(méi)有指定返回類型,那么將返回Task。每個(gè)返回的任務(wù)都表示正在進(jìn)行的工作,任務(wù)封裝有關(guān)異步進(jìn)程狀態(tài)的信息,如果未成功,則會(huì)引發(fā)異常。異步方法返回 Task 或 Task<TResult>。返回任務(wù)的屬性攜帶有關(guān)其狀態(tài)和歷史記錄的信息,如任務(wù)是否完成、異步方法是否導(dǎo)致異?;蛞讶∠约白罱K結(jié)果是什么??墒褂胊wait運(yùn)算符訪問(wèn)這些屬性。

   1:  public async static Task<User> GetUserInfoAsync()
   2:  {
   3:      User user = await db.User.FirstOrDefautAsync();
   4:
   5:      return user;
   6:  }

2、如果等待的任務(wù)返回異步方法導(dǎo)致異常,則 await 運(yùn)算符會(huì)以同步方式拋出異常。如果等待的返回任務(wù)的異步方法取消,await運(yùn)算符引發(fā)OperationCanceledException。如果異步方法中沒(méi)有使用await阻塞,可以使用try-catch捕捉異常,只是異常發(fā)生的時(shí)機(jī)可能會(huì)滯后。

異步方法的運(yùn)行流程

了解異步方法的運(yùn)行機(jī)制,就是要了解異步編程中的控制流是如何一步步執(zhí)行的。如果需要詳細(xì)了解控制流,可以異步到MSDN中查看。

下圖及其描述摘自MSDN:

C#中怎么實(shí)現(xiàn)異步編程

關(guān)系圖中的數(shù)值對(duì)應(yīng)于以下步驟。

  1. 事件處理程序調(diào)用并等待 AccessTheWebAsync 異步方法。

  2. AccessTheWebAsync 創(chuàng)建HttpClient實(shí)例并調(diào)用GetStringAsync異步方法,獲取的內(nèi)容字符串方式返回。

  3. GetStringAsync 中發(fā)生了某種情況,該情況掛起了它的進(jìn)程??赡鼙仨毜却渌柚谷蝿?wù)完成。為避免阻止資源,GetStringAsync 會(huì)將控制權(quán)出讓給其調(diào)用方 AccessTheWebAsync。 GetStringAsync 返回Task<TResult>,其中 TResult 為字符串,并且 AccessTheWebAsync 將任務(wù)分配給 getStringTask 變量。該任務(wù)將調(diào)用GetStringAsync正在進(jìn)行的進(jìn)程,在調(diào)用完成時(shí)產(chǎn)生返回字符串給urlcontent。

  4. 由于尚未等待 getStringTask,因此,AccessTheWebAsync 可以繼續(xù)執(zhí)行而不依賴于 GetStringAsync 最終結(jié)果的完成。該任務(wù)繼續(xù)調(diào)用同步方法 DoIndependentWork。

  5. DoIndependentWork 作為一個(gè)同步方法,在自身工作完成后返回到調(diào)用方。

  6. AccessTheWebAsync 已運(yùn)行完畢,可以不受 getStringTask 的結(jié)果影響。接下來(lái),AccessTheWebAsync 需要計(jì)算并返回已下載的字符串的長(zhǎng)度,但該方法只有在獲得字符串的情況下才能計(jì)算該值。

    因此,AccessTheWebAsync 使用一個(gè) await 運(yùn)算符來(lái)掛起其任務(wù),并把控制權(quán)交給調(diào)用 AccessTheWebAsync 的事件處理程序。 AccessTheWebAsync 將 Task<int>返回給調(diào)用方。該任務(wù)將計(jì)算下載字符串長(zhǎng)度。 

  7. GetStringAsync 完成并生成一個(gè)字符串結(jié)果。字符串結(jié)果不是通過(guò)按你預(yù)期的方式調(diào)用 GetStringAsync 所返回的。(記住,該方法已返回步驟 3 中的一個(gè)任務(wù))。相反,字符串結(jié)果存儲(chǔ)在表示 getStringTask 方法完成的任務(wù)中。await 運(yùn)算符從 getStringTask 中檢索結(jié)果。賦值語(yǔ)句將檢索到的結(jié)果賦給 urlContents。

  8. 當(dāng) AccessTheWebAsync 獲取字符串結(jié)果時(shí),該方法可以計(jì)算字符串長(zhǎng)度。然后,AccessTheWebAsync 工作也將完成,并且等待事件處理程序的繼續(xù)使用。事件處理程序也將最終獲得字符串的長(zhǎng)度信息。

注意:

如果 GetStringAsync(因此 getStringTask)在 AccessTheWebAsync 等待前完成,則控制權(quán)會(huì)保留在 AccessTheWebAsync中。如果異步調(diào)用過(guò)程 (AccessTheWebAsync) 已完成,并且 AccessTheWebSync 不必等待最終結(jié)果,則掛起然后返回到 getStringTask 將造成資源浪費(fèi)。

在調(diào)用方內(nèi)部(此示例中的事件處理程序),處理模式將繼續(xù)。在等待結(jié)果前,調(diào)用方可以開(kāi)展不依賴于 AccessTheWebAsync 結(jié)果的其他工作,否則就需等待片刻。事件處理程序等待 AccessTheWebAsync,而 AccessTheWebAsync 等待 GetStringAsync。

異步編程對(duì)性能的影響

 在.NET異步編程中,async和await不會(huì)創(chuàng)建其他線程,同時(shí)異步方法不會(huì)在其自身線程上運(yùn)行,因此它不需要多線程。只有當(dāng)方法處于活動(dòng)狀態(tài)時(shí),該方法將在當(dāng)前同步上下文中運(yùn)行并使用線程上的時(shí)間??梢允褂肨ask.Run將占用大量CPU的工作移到后臺(tái)線程,但是后臺(tái)線程不會(huì)幫助正在等待結(jié)果的進(jìn)程變?yōu)榭捎脿顟B(tài)。

對(duì)于異步編程而言,基于異步的方法優(yōu)于幾乎每個(gè)用例中的現(xiàn)有方法。具體而言,這種方法優(yōu)于BackgroundWorker的I/O綁定操作因?yàn)榇a更簡(jiǎn)單且無(wú)需防止?fàn)幱脳l件。結(jié)合Task.Run使用時(shí),異步編程比BackgroundWorker更適用于CPU綁定的操作,因?yàn)楫惒骄幊虒⑦\(yùn)行代碼的協(xié)調(diào)細(xì)節(jié)與Task.Run傳輸至線程池的工作區(qū)分開(kāi)來(lái)。

那么異步編程對(duì)線程的影響又是什么呢,相比大家應(yīng)該都知道,ASP.NET中有兩類線程,工作線程,和IO線程。

其中工作線程處理普通請(qǐng)求的線程,也是我們用得最多的線程。這個(gè)線程是有限的,是根CPU的個(gè)數(shù)相關(guān)的。IO線程,比如與文件讀寫(xiě),網(wǎng)絡(luò)操作等是可以異步實(shí)現(xiàn)并且使性能提升的地方。I/O線程通常情況下是空閑的。所以可以使用IO線程來(lái)代替工作線程,一方面充分運(yùn)用了系統(tǒng)資源,另一方面也節(jié)省了工作線程調(diào)度及切換所帶來(lái)的損耗。

由此我們需要明白,在I/O密集型處理時(shí),使用異步可以帶來(lái)很大的提升,比如數(shù)據(jù)庫(kù)操作以及網(wǎng)絡(luò)操作。

即便異步編程帶來(lái)性能的提升,但是運(yùn)用不慎,也會(huì)對(duì)系統(tǒng)性能產(chǎn)生反作用,比如直接使用Task.Run或者Task.Factory.StartNew所帶來(lái)的異步編程,這些方式會(huì)占用工作線程以及工作線程之間的切換。 

異步編程需要注意的地方


 

1、同時(shí)async和await侵入性或者傳遞性很強(qiáng),所有調(diào)用的地方都需要同步使用async和await,這對(duì)系統(tǒng)中老代碼的修改產(chǎn)生了很大的影響。

2、異步編程中無(wú)法使用lock鎖,因?yàn)楫惒椒椒ú粫?huì)在自身線程上運(yùn)行,lock就變成了多余的了。但異步編程場(chǎng)景下可以使用AsyncLock鎖,對(duì)相應(yīng)的代碼進(jìn)行鎖定。

3、異步編程里,比較推薦的做法是避免上線文延續(xù),此處不再做更多說(shuō)明,參考我的前一篇文章《異步編程(一)》

4、異步編程是否真的提升了系統(tǒng)性能,目前來(lái)看大多數(shù)場(chǎng)景下是提升了,尤其在I/O操作比較密集的業(yè)務(wù)場(chǎng)景下,比如查詢數(shù)據(jù)庫(kù)和網(wǎng)絡(luò)調(diào)用。

關(guān)于C#中怎么實(shí)現(xiàn)異步編程就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI