原文鏈接
大多數(shù)有關(guān) async/await 的在線(xiàn)資源假定您正在開(kāi)發(fā)客戶(hù)端應(yīng)用程序,但在服務(wù)器上有 async 的位置嗎?可以非常肯定地回答“有”。本文是對(duì) ASP.NET 上異步請(qǐng)求的概念性概述,并提供了對(duì)最佳在線(xiàn)資源的引用。我不打算介紹 async 或 await 的語(yǔ)法;因?yàn)槲乙呀?jīng)在一篇介紹性的博客文章 (bit.ly/19IkogW) 以及一篇關(guān)于 async 最佳做法的文章 (msdn.microsoft.com/magazine/jj991977) 中介紹過(guò)了。本文將特別重點(diǎn)介紹 async 在 ASP.NET 上的工作原理。
對(duì)于客戶(hù)端應(yīng)用程序,如 Windows 應(yīng)用商店、Windows 桌面和 Windows Phone 應(yīng)用程序,async 的主要優(yōu)點(diǎn)是出色的響應(yīng)能力。這些類(lèi)型的應(yīng)用程序使用 async 主要是為了保證用戶(hù)界面的響應(yīng)能力。對(duì)于服務(wù)器應(yīng)用程序,async 異步的主要優(yōu)點(diǎn)是不錯(cuò)的可擴(kuò)展性。Node.js 可擴(kuò)展性的關(guān)鍵是其固有的異步本質(zhì);Open Web Interface for .NET (OWIN) 針對(duì)異步進(jìn)行了全新設(shè)計(jì);ASP.NET 也可以是異步的。Async:不僅僅適用于 UI 應(yīng)用程序!
在深入探討異步請(qǐng)求處理程序之前,我想簡(jiǎn)要地回顧同步請(qǐng)求處理程序在 ASP.NET 上的工作原理。在本例中,假設(shè)系統(tǒng)中的請(qǐng)求依賴(lài)于一些外部資源,如數(shù)據(jù)庫(kù)或 Web API。當(dāng)收到請(qǐng)求時(shí),ASP.NET 將其中的一個(gè)線(xiàn)程池線(xiàn)程分配給該請(qǐng)求。因?yàn)樗峭骄帉?xiě),所以請(qǐng)求處理程序?qū)⑼秸{(diào)用該外部資源。這將阻止請(qǐng)求線(xiàn)程,直到返回對(duì)外部資源的調(diào)用。圖 1說(shuō)明了具有兩個(gè)線(xiàn)程的線(xiàn)程池,其中有一個(gè)線(xiàn)程被阻止,正在等待外部資源。
圖 1 同步等待外部資源
最后,返回該外部資源的調(diào)用,并且請(qǐng)求線(xiàn)程恢復(fù)處理該請(qǐng)求。當(dāng)完成該請(qǐng)求,且準(zhǔn)備好發(fā)送響應(yīng)時(shí),請(qǐng)求線(xiàn)程將返回到線(xiàn)程池中。
這一切好倒是好,但是您的 ASP.NET 服務(wù)器獲得的請(qǐng)求總會(huì)超出它的線(xiàn)程能夠處理的數(shù)量。這時(shí)候,額外的請(qǐng)求必須等到有線(xiàn)程可用時(shí)才能夠運(yùn)行。圖 2說(shuō)明的仍是該雙線(xiàn)程服務(wù)器,此時(shí)它收到三個(gè)請(qǐng)求。
圖 2 收到三個(gè)請(qǐng)求的雙線(xiàn)程服務(wù)器
在這種情況下,前兩個(gè)請(qǐng)求都被分配到線(xiàn)程池中的線(xiàn)程。每個(gè)請(qǐng)求都調(diào)用外部資源,于是阻止了它們的線(xiàn)程。第三個(gè)請(qǐng)求必須等待有線(xiàn)程可用時(shí),才可以開(kāi)始進(jìn)行處理,但該請(qǐng)求已經(jīng)在系統(tǒng)中。它的計(jì)時(shí)器一直在工作,它正處于發(fā)生 HTTP 錯(cuò)誤 503(服務(wù)不可用)的危險(xiǎn)之中。
但是對(duì)此考慮一下:這第三個(gè)請(qǐng)求正在等待可用線(xiàn)程,與此同時(shí)系統(tǒng)中的另外兩個(gè)線(xiàn)程實(shí)際上什么都沒(méi)做。這些線(xiàn)程受到阻止,都在等待返回外部調(diào)用。它們確實(shí)沒(méi)有做任何實(shí)際工作;它們不處于運(yùn)行狀態(tài),也不占用任何 CPU 時(shí)間。這些線(xiàn)程被白白浪費(fèi)掉,但還有請(qǐng)求處于需要中。以下是異步請(qǐng)求解決的情況。
異步請(qǐng)求處理程序的操作方式與此不同。當(dāng)收到請(qǐng)求時(shí),ASP.NET 將其中的一個(gè)線(xiàn)程池線(xiàn)程分配給該請(qǐng)求。這一次,請(qǐng)求處理程序?qū)惒秸{(diào)用該外部資源。當(dāng)返回對(duì)外部資源的調(diào)用之前,已將此請(qǐng)求線(xiàn)程返回到線(xiàn)程池中。圖 3說(shuō)明當(dāng)請(qǐng)求在異步等待外部資源時(shí)具有兩個(gè)線(xiàn)程的線(xiàn)程池。
圖 3 異步等待外部資源
重要的區(qū)別在于,在進(jìn)行異步調(diào)用的過(guò)程中,已將請(qǐng)求線(xiàn)程返回到線(xiàn)程池中。當(dāng)線(xiàn)程處于線(xiàn)程池中時(shí),它不再與該請(qǐng)求相關(guān)聯(lián)。此時(shí),當(dāng)返回外部資源調(diào)用時(shí),ASP.NET 將其線(xiàn)程池中的一個(gè)線(xiàn)程重新分配給該請(qǐng)求。該線(xiàn)程將繼續(xù)處理該請(qǐng)求。當(dāng)請(qǐng)求完成時(shí),該線(xiàn)程再次返回到線(xiàn)程池中。注意,對(duì)于同步處理程序,同一線(xiàn)程會(huì)用于該請(qǐng)求的整個(gè)生命周期;相反,對(duì)于異步處理程序,可以將不同的線(xiàn)程分配給同一請(qǐng)求(在不同的時(shí)間)。
現(xiàn)在,如果三個(gè)請(qǐng)求都進(jìn)來(lái),服務(wù)器就可以輕松處理了。因?yàn)槊慨?dāng)請(qǐng)求在等待異步工作時(shí),線(xiàn)程就會(huì)被釋放到線(xiàn)程池中,它們可以自由處理新的以及現(xiàn)有的請(qǐng)求。異步請(qǐng)求可以讓數(shù)量較少的線(xiàn)程去處理數(shù)量較多的請(qǐng)求。因此,ASP.NET 上的異步代碼的主要優(yōu)點(diǎn)是出色的可擴(kuò)展性。
此時(shí),總是會(huì)被問(wèn)到:為什么不增加線(xiàn)程池的大小?答案有兩個(gè):與阻止線(xiàn)程池線(xiàn)程相比,異步代碼擴(kuò)展得更深層且更快。
異步代碼的擴(kuò)展性超過(guò)阻止線(xiàn)程,這是因?yàn)樗褂玫膬?nèi)存更少;在現(xiàn)代操作系統(tǒng)上每個(gè)線(xiàn)程池線(xiàn)程具有 1MB 的堆棧,外加一個(gè)不分頁(yè)的內(nèi)核堆棧。這聽(tīng)起來(lái)好像很多,但當(dāng)您的服務(wù)器上有一大堆線(xiàn)程時(shí),會(huì)發(fā)現(xiàn)其實(shí)不夠用。與此相反,異步操作的內(nèi)存開(kāi)銷(xiāo)要小得多。因此,使用異步操作的請(qǐng)求比使用阻止線(xiàn)程的請(qǐng)求面臨更少的內(nèi)存壓力。異步代碼使您可以將更多的內(nèi)存用于其他任務(wù)(例如緩存)。
異步代碼在速度上比阻止線(xiàn)程更快,因?yàn)榫€(xiàn)程池的注入速度有限。截至發(fā)稿時(shí),該速度為每?jī)擅腌娨粋€(gè)線(xiàn)程。注入速度有限是件好事;它避免了持續(xù)的線(xiàn)程構(gòu)建和破壞。然而,考慮一下請(qǐng)求蜂擁而至?xí)r會(huì)發(fā)生什么。同步代碼很容易就會(huì)陷入癱瘓,因?yàn)檎?qǐng)求將用光所有可用的線(xiàn)程,其余請(qǐng)求必須等待線(xiàn)程池有新的線(xiàn)程注入。而另一方面,異步代碼不需要有這樣的限制;它是“始終開(kāi)放”的,可以這么說(shuō)。異步代碼能夠更出色地響應(yīng)請(qǐng)求量突然波動(dòng)。
請(qǐng)記住,異步代碼不會(huì)取代線(xiàn)程池。不應(yīng)該只有線(xiàn)程池或異步代碼;而是要同時(shí)擁有線(xiàn)程池和異步代碼。異步代碼可以讓您的應(yīng)用程序充分利用線(xiàn)程池。它使用現(xiàn)有的線(xiàn)程池,并把它提高到 11。
我一直被人問(wèn)到這個(gè)問(wèn)題。這意味著,必須有一些線(xiàn)程阻止對(duì)外部資源進(jìn)行 I/O 調(diào)用。因此,異步代碼釋放請(qǐng)求線(xiàn)程,但這只能以犧牲系統(tǒng)中另一個(gè)線(xiàn)程為代價(jià)吧?沒(méi)有,一點(diǎn)關(guān)系也沒(méi)有。
要了解異步請(qǐng)求為什么擴(kuò)展,我將跟蹤一個(gè)異步 I/O 調(diào)用的(簡(jiǎn)化)示例。假設(shè)有一個(gè)請(qǐng)求需要寫(xiě)入到文件中。請(qǐng)求線(xiàn)程調(diào)用異步寫(xiě)入方法。WriteAsync 由基類(lèi)庫(kù) (BCL) 實(shí)現(xiàn),并使用其異步 I/O 的完成端口。因此,WriteAsync 調(diào)用會(huì)作為異步文件寫(xiě)入傳遞到 OS 中。然后,OS 與驅(qū)動(dòng)程序堆棧進(jìn)行通信,同時(shí)傳遞數(shù)據(jù)以寫(xiě)入到 I/O 請(qǐng)求數(shù)據(jù)包 (IRP) 中。
現(xiàn)在,有趣的事情發(fā)生了:如果設(shè)備驅(qū)動(dòng)程序不能立即處理 IRP,就必須異步進(jìn)行處理。因此,驅(qū)動(dòng)程序告訴磁盤(pán)開(kāi)始寫(xiě)入,并將“掛起”響應(yīng)返回到 OS 中。OS 將“掛起”響應(yīng)傳遞到 BCL,然后 BCL 將一個(gè)不完整的任務(wù)返回到請(qǐng)求處理代碼。請(qǐng)求處理代碼等待將不完整的任務(wù)從該方法等處返回的任務(wù)。最后,請(qǐng)求處理代碼最終向 ASP.NET 返回一個(gè)不完整的任務(wù),并且請(qǐng)求線(xiàn)程被釋放回線(xiàn)程池中。
現(xiàn)在,考慮系統(tǒng)的當(dāng)前狀態(tài)。已經(jīng)分配了各種 I/O 結(jié)構(gòu)(例如,任務(wù)實(shí)例和 IRP),而且它們都處在掛起/不完整的狀態(tài)。然而,沒(méi)有任何線(xiàn)程因等待寫(xiě)入操作完成而受到阻止。ASP.NET、BCL、OS 以及設(shè)備驅(qū)動(dòng)程序都沒(méi)有專(zhuān)門(mén)用于異步工作的線(xiàn)程。
當(dāng)磁盤(pán)完成寫(xiě)入數(shù)據(jù)時(shí),它通過(guò)中斷通知其驅(qū)動(dòng)程序。該驅(qū)動(dòng)程序會(huì)通知 OS 該 IRP 已經(jīng)完成,并且 OS 會(huì)通過(guò)完成端口通知 BCL。線(xiàn)程池線(xiàn)程通過(guò)完成從 WriteAsync 返回的任務(wù)來(lái)響應(yīng)該通知;這反過(guò)來(lái)又恢復(fù)異步請(qǐng)求代碼。在該完成通知階段中,短期“借用”了一些線(xiàn)程,但實(shí)際上沒(méi)有線(xiàn)程在寫(xiě)入過(guò)程中受到阻止。
本示例經(jīng)過(guò)極大地簡(jiǎn)化,但是要點(diǎn)突出:真正的異步工作并不需要線(xiàn)程。實(shí)際推送字節(jié)也無(wú)需占用 CPU 時(shí)間。還有一個(gè)輔助課程要了解。考慮一下在設(shè)備驅(qū)動(dòng)程序的世界里,設(shè)備驅(qū)動(dòng)程序如何做才能立即或異步處理 IRP。同步處理是不是一個(gè)選項(xiàng)。在設(shè)備驅(qū)動(dòng)程序級(jí)別,所有重要的 I/O 都是異步的。許多開(kāi)發(fā)人員的思維模式都是把用于 I/O 操作的“普通 API”認(rèn)為是同步的,異步 API 作為一層建立在普通的同步 API 上。然而,這恰恰相反:實(shí)際上,普通 API是??異步的;使用異步 I/O 實(shí)現(xiàn)的是正是同步 API!
如果異步請(qǐng)求處理是如此完美,那它為什么還不可用?事實(shí)上,異步代碼非常適合擴(kuò)展,因此從 Microsoft .NET Framework 形成之初到現(xiàn)在,ASP.NET 平臺(tái)一直支持異步處理程序和模塊。ASP.NET 2.0 引入了異步網(wǎng)頁(yè),ASP.NET MVC 2 中 MVC 得到了異步控制器。
然而,最近,異步代碼在編寫(xiě)上總是有些問(wèn)題,并且難于維護(hù)。許多公司便決定同步開(kāi)發(fā)代碼、支付更大的服務(wù)器場(chǎng)或更昂貴的托管,這樣就會(huì)簡(jiǎn)單一些。現(xiàn)在,出現(xiàn)了逆轉(zhuǎn):在 ASP.NET 4.5 中,使用 async 和 await 的異步代碼幾乎與編寫(xiě)同步代碼一樣簡(jiǎn)單。由于大型系統(tǒng)遷移到云托管并要求更加有規(guī)模,越來(lái)越多的公司開(kāi)始青睞 ASP.NET 上的 async 和 await。
異步請(qǐng)求處理盡管很強(qiáng)大,但它不會(huì)解決所有問(wèn)題。關(guān)于 ASP.NET 上的 async 和 await 可以做什么的問(wèn)題,存在一些常見(jiàn)的誤解。
當(dāng)一些開(kāi)發(fā)人員了解 async 和 await 后,他們認(rèn)為這是服務(wù)器代碼“讓步”于客戶(hù)端(例如瀏覽器)的一種方式。然而,ASP.NET 上的 async 和 await 只“讓步”于 ASP.NET 運(yùn)行時(shí);HTTP 協(xié)議保持不變,您對(duì)每個(gè)請(qǐng)求仍只有一個(gè)響應(yīng)。如果在 async/await 之前您需要 SignalR 或 Ajax 或 UpdatePanel,那么在 async/await 之后仍然需要 SignalR 或 AJAX 或 UpdatePanel。
使用 async 和 await 的異步請(qǐng)求處理可以幫助擴(kuò)大您的應(yīng)用程序規(guī)模。然而,這是在一臺(tái)服務(wù)器上的擴(kuò)展;您可能仍然需要對(duì)擴(kuò)展進(jìn)行規(guī)劃。如果您確實(shí)需要擴(kuò)展體系結(jié)構(gòu),將仍然需要考慮無(wú)狀態(tài)的冪等請(qǐng)求和可靠的隊(duì)列。Async/await 多少有所幫助:它們使您能夠充分利用服務(wù)器資源,所以您不需要經(jīng)常進(jìn)行擴(kuò)展。但是,如果您確實(shí)需要向外擴(kuò)展,您將需要一個(gè)合適的分布式體系結(jié)構(gòu)。
ASP.NET 上的 async 和 await 都是關(guān)于 I/O 的。它們非常適合讀取和寫(xiě)入文件、數(shù)據(jù)庫(kù)記錄和 REST API。然而,它們不能很好地執(zhí)行占用大量 CPU 的任務(wù)。您可以通過(guò)等待 Task.Run 開(kāi)始一些背景工作,但這樣做沒(méi)有任何意義。事實(shí)上,通過(guò)啟發(fā)式干擾 ASP.NET 線(xiàn)程池會(huì)損害您的可擴(kuò)展性。如果您要在 ASP.NET 上執(zhí)行占用大量 CPU 的工作,最好的辦法是直接在請(qǐng)求線(xiàn)程上執(zhí)行該工作。通常,不要將工作排隊(duì)送到 ASP.NET 上的線(xiàn)程池。
最后,在整體上考慮系統(tǒng)的可擴(kuò)展性。十年前,常見(jiàn)的體系結(jié)構(gòu)要有一個(gè)可與后端的 SQL Server 數(shù)據(jù)庫(kù)進(jìn)行通信的 ASP.NET Web 服務(wù)器。在這種簡(jiǎn)單的體系結(jié)構(gòu)中,通常數(shù)據(jù)庫(kù)服務(wù)器是可擴(kuò)展性的瓶頸,而不是 Web 服務(wù)器。讓您的數(shù)據(jù)庫(kù)調(diào)用異步可能起不到幫助作用;當(dāng)然您可以用它們來(lái)擴(kuò)展 Web 服務(wù)器,但數(shù)據(jù)庫(kù)服務(wù)器將阻止整個(gè)系統(tǒng)的擴(kuò)展。
Rick Anderson 在他精彩的博客文章中針對(duì)異步數(shù)據(jù)庫(kù)調(diào)用給出案例,“我的數(shù)據(jù)庫(kù)調(diào)用應(yīng)該是異步的嗎?”(bit.ly/1rw66UB)。以下是兩點(diǎn)支持論據(jù):首先,異步代碼有難度(因而開(kāi)發(fā)人員的時(shí)間成本比只是購(gòu)買(mǎi)較大的服務(wù)器要高);其次,如果數(shù)據(jù)庫(kù)后端是瓶頸,那么擴(kuò)展 Web 服務(wù)器沒(méi)有什么意義。在寫(xiě)這篇文章時(shí),這兩方面的論據(jù)非常有道理,但隨著時(shí)間的推移這兩個(gè)論據(jù)的意義已經(jīng)慢慢弱化。首先,使用 async 和 await 編寫(xiě)異步代碼更加容易了。其次,隨著全球逐步采用云計(jì)算,網(wǎng)站的數(shù)據(jù)后端逐漸得到擴(kuò)展。諸如 Microsoft Azure SQL 數(shù)據(jù)庫(kù)、NoSQL 以及其他 API 之類(lèi)的現(xiàn)代后端與單個(gè) SQL Server 相比可以得到更進(jìn)一步的擴(kuò)展,從而將瓶頸又推回 Web 服務(wù)器。在這種情況下,async/await 可以通過(guò)擴(kuò)展 ASP.NET 帶來(lái)巨大的優(yōu)勢(shì)。
首先您需要知道只有 ASP.NET 4.5 支持 async 和 await。有一個(gè)叫做 Microsoft.Bcl.Async 的 NuGet 程序包可為 .NET Framework 4 啟用 async 和 await,但并不使用它;這將無(wú)法正常工作!其原因是,為了能與 async 和 await 更好地一起工作,ASP.NET 本身必須更改其管理異步請(qǐng)求處理的方式;NuGet 程序包中包含編譯器需要的所有類(lèi)型,但不會(huì)修補(bǔ) ASP.NET 運(yùn)行時(shí)。沒(méi)有解決方法;您需要 ASP.NET 4.5 或更高版本。
接下來(lái),要知道,ASP.NET 4.5 在服務(wù)器上引入了“quirks 模式”。如果您創(chuàng)建一個(gè)新的 ASP.NET 4.5 項(xiàng)目,則不必?fù)?dān)心。但是,如果要將現(xiàn)有的項(xiàng)目升級(jí)到 ASP.NET 4.5,所有 quirk 都將被打開(kāi)。我建議您??通過(guò)編輯 web.config 并將 httPRuntime.targetFramework 設(shè)置為 4.5 把它們?nèi)筷P(guān)閉。如果使用此設(shè)置的應(yīng)用程序失敗(并且您不想花時(shí)間去修復(fù)它),至少您可以通過(guò)為 aspnet:UseTaskFriendlySynchronizationContext 的 appSetting 鍵添加值“true”來(lái)獲取 async/await 工作。如果您將 httpRuntime.targetFramework 設(shè)置為 4.5,則 appSetting 鍵不必要。Web 開(kāi)發(fā)團(tuán)隊(duì)已在bit.ly/1pbmnzK發(fā)表一篇關(guān)于這一新的“quirks 模式”的詳細(xì)信息的博客。提示: 如果您看到出現(xiàn)奇怪的行為或例外情況,并且您的調(diào)用堆棧包括 LegacyAspNetSynchronizationContext,那么您的應(yīng)用程序正在這個(gè)“quirks 模式”下運(yùn)行。LegacyAspNetSynchronizationContext 與異步不兼容;您在 ASP.NET 4.5 上需要常規(guī)的 AspNetSynchronizationContext。
在 ASP.NET 4.5 中,所有的 ASP.NET 設(shè)置都針對(duì)異步請(qǐng)求設(shè)置了很好的默認(rèn)值,但也有幾個(gè)其他設(shè)置您可能要更改。首先是 IIS 設(shè)置:考慮將 IIS/HTTP.sys 的隊(duì)列限制(應(yīng)用程序池|高級(jí)設(shè)置|隊(duì)列長(zhǎng)度)從默認(rèn)的 1,000 提高到 5,000。另一個(gè)是 .NET 運(yùn)行時(shí)設(shè)置:ServicePointManager.DefaultConnectionLimit,它的默認(rèn)值是內(nèi)核數(shù)量的 12 倍。DefaultConnectionLimit 限制到同一主機(jī)名的傳出連接數(shù)。
當(dāng) ASP.NET 同步處理一個(gè)請(qǐng)求時(shí),它有一個(gè)非常簡(jiǎn)單的機(jī)制可以中止請(qǐng)求(例如,如果請(qǐng)求超出其超時(shí)值):它會(huì)中止該請(qǐng)求的工作線(xiàn)程。這是有道理的,因?yàn)樵谕筋I(lǐng)域,每個(gè)請(qǐng)求從開(kāi)始到結(jié)束都使用同一個(gè)工作線(xiàn)程。中止線(xiàn)程對(duì)于 AppDomain 的長(zhǎng)期穩(wěn)定性而言尚不完美,因此默認(rèn)情況下 ASP.NET 將定期回收您的應(yīng)用程序,以保持干凈。
對(duì)于異步請(qǐng)求,如果要中止請(qǐng)求,ASP.NET 并不會(huì)中止工作線(xiàn)程。相反,它會(huì)取消使用 CancellationToken 的請(qǐng)求。異步請(qǐng)求處理程序應(yīng)該接受并遵守取消標(biāo)記。大多數(shù)較新的框架(包括 Web API、MVC 和 SignalR)將構(gòu)建并直接向您傳遞 CancellationToken;您需要做的就是把它聲明為一個(gè)參數(shù)。您也可以直接訪(fǎng)問(wèn) ASP.NET 標(biāo)記;例如,HttpRequest.TimedOutToken 是當(dāng)請(qǐng)求超時(shí)時(shí)取消的一個(gè) CancellationToken。
隨著應(yīng)用程序遷移到云,中止請(qǐng)求就顯得更為重要。基于云的應(yīng)用程序也越來(lái)越依賴(lài)于可能占用任意時(shí)間量的外部服務(wù)。例如,一種標(biāo)準(zhǔn)模式是使用指數(shù)回退來(lái)重試外部請(qǐng)求;如果您的應(yīng)用程序依賴(lài)于類(lèi)似這樣的多種服務(wù),對(duì)您的請(qǐng)求處理在整體上應(yīng)用一個(gè)超時(shí)上限不失為一個(gè)好方法。
針對(duì) async 的兼容性問(wèn)題,已對(duì)許多庫(kù)進(jìn)行了更新。在版本 6 中已將 async 支持添加到實(shí)體框架(在 EntityFramework NuGet 程序包中)。不過(guò),當(dāng)以異步方式運(yùn)行時(shí),您必須要小心操作以避免延遲加載,因?yàn)檠舆t加載總是以同步方式執(zhí)行。HttpClient(在 Microsoft.Net.Http NuGet 程序包中)是采用 async 理念設(shè)計(jì)而成的現(xiàn)代 HTTP 客戶(hù)端,是調(diào)用外部 REST API 的理想選擇;是 HttpWebRequest 和 WebClient 的現(xiàn)代版替代品。在 2.1 版本中,Microsoft Azure 存儲(chǔ)客戶(hù)端庫(kù)(在 WindowsAzure.Storage NuGet 程序包中)添加了異步支持。
較新的框架(如 Web API 和 SignalR)對(duì) async 和 await 提供全面的支持。個(gè)別 Web API 已圍繞 async 支持建立起整個(gè)管道:不僅有異步控制器,還有異步篩選器和處理程序。Web API 和 SignalR 有一個(gè)很平凡的異步故事:您可以“放手去做”然后“就會(huì)成功”。
這給我們帶來(lái)了一個(gè)令人傷感的故事:如今,ASP.NET MVC 只是部分支持 async 和 await。有基本的支持——異步控制器的操作和取消工作正常。ASP.NET 網(wǎng)站上有關(guān)于如何使用 ASP.NET MVC 中的異步控制器操作的精彩教程 (bit.ly/1m1LXTx);這對(duì)于 MVC 上的 async 入門(mén)是絕佳的資源。不幸的是
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注