利用 async amp; await 的异步编程
一、異步編程的簡(jiǎn)介
通過使用異步編程,你可以避免性能瓶頸并增強(qiáng)應(yīng)用程序的總體響應(yīng)能力。
Visual Studio 2012 引入了一個(gè)簡(jiǎn)化的方法,異步編程,在 .NET Framework 4.5 和 Windows 運(yùn)行時(shí)利用異步支持。編譯器可執(zhí)行開發(fā)人員曾進(jìn)行的高難度工作,且應(yīng)用程序保留了一個(gè)類似于同步代碼的邏輯結(jié)構(gòu)。因此,您僅需要進(jìn)行一小部分工作就可以獲得異步編程的所有優(yōu)點(diǎn)。
?
二、異步提高響應(yīng)能力
異步對(duì)可能引起阻塞的活動(dòng)(例如應(yīng)用程序訪問 Web 時(shí))至關(guān)重要。對(duì) Web 資源的訪問有時(shí)很慢或會(huì)延遲。如果此類活動(dòng)在同步過程中受阻,則整個(gè)應(yīng)用程序必須等待。?在異步過程中,應(yīng)用程序可繼續(xù)執(zhí)行不依賴 Web 資源的其他工作,直至潛在阻塞的任務(wù)完成。
下圖顯示了異步編程提高響應(yīng)能力的典型應(yīng)用場(chǎng)景。包含從 .NET Framework 4.5 和 Windows 運(yùn)行時(shí)中列出的一些包含支持異步編程的方法的類。
由于所有與用戶界面相關(guān)的活動(dòng)通常共享一個(gè)線程,因此,異步對(duì)訪問 UI 線程的應(yīng)用程序來說尤為重要。?如果在一個(gè)同步應(yīng)用程序中有任何的線程被阻塞了,那么所有線程都將被阻塞,再嚴(yán)重一點(diǎn),你的應(yīng)用程序?qū)?huì)停止響應(yīng)。
使用異步方法時(shí),應(yīng)用程序?qū)⒗^續(xù)響應(yīng) UI。例如,你可以調(diào)整窗口的大小或最小化窗口;如果你不希望等待應(yīng)用程序結(jié)束,則可以將其關(guān)閉。
?
三、更容易編寫的異步方法
C# 中的?async?和?await?關(guān)鍵字都是異步編程的核心。通過使用這兩個(gè)關(guān)鍵字,你可以使用 .NET framework 或 Windows 運(yùn)行時(shí)中的資源輕松創(chuàng)建異步方法(幾乎與創(chuàng)建同步方法一樣輕松)。
下面的示例演示了一種使用 async 和 await 定義的異步方法。
/// <summary>
? ? ? ? /// 異步訪問 Web?
? ? ? ? /// </summary>
? ? ? ? /// <returns></returns>
? ? ? ? /// <remarks>
? ? ? ? /// 方法簽名的 3 要素:
? ? ? ? /// ? ? ① async 修飾符
? ? ? ? /// ? ? ② 返回類型 Task 或 Task<TResult>:這里的 Task<int> 表示 return 語句返回 int 類型
? ? ? ? /// ? ? ③ 方法名以 Async 結(jié)尾
? ? ? ? /// </remarks>
? ? ? ? async Task<int> AccessTheWebAsync()
? ? ? ? {
? ? ? ? ? ? //記得 using System.Net.Http 哦
? ? ? ? ? ? var client = new HttpClient();
? ? ? ? ? ? //執(zhí)行異步方法 GetStringAsync
? ? ? ? ? ? Task<string> getStringTask = client.GetStringAsync("http://www.google.com.hk/");
? ? ? ? ? ? //假設(shè)在這里執(zhí)行一些非異步的操作
? ? ? ? ? ? DoIndependentWork();
? ? ? ? ? ? //等待操作掛起方法 AccessTheWebAsync
? ? ? ? ? ? //直到 getStringTask 完成,AccessTheWebAsync 方法才會(huì)繼續(xù)執(zhí)行
? ? ? ? ? ? //同時(shí),控制將返回到 AccessTheWebAsync 方法的調(diào)用方
? ? ? ? ? ? //直到 getStringTask 完成后,將在這里恢復(fù)控制。
? ? ? ? ? ? //然后從 getStringTask 拿到字符串結(jié)果
? ? ? ? ? ? string urlContents = await getStringTask;
? ? ? ? ? ? //返回字符串的長(zhǎng)度(int 類型)
? ? ? ? ? ? return urlContents.Length;
? ? ? ? }
如果?AccessTheWebAsync?在調(diào)用?GetStringAsync 時(shí)沒有其它操作,你可以用這樣的方式來簡(jiǎn)化代碼。
string urlContents = await client.GetStringAsync("http://www.google.com.hk/");
根據(jù)以上代碼進(jìn)行簡(jiǎn)單總結(jié):
(1)方法簽名包含一個(gè)?async?修飾符。
(2)按照約定,異步方法的名稱以“Async”后綴為結(jié)尾。
(3)返回類型為下列類型之一:
①?如果你的方法有操作數(shù)為 TResult 類型的返回語句,則為?Task<TResult>。
②?如果你的方法沒有返回語句或具有沒有操作數(shù)的返回語句,則為?Task。
③?如果你編寫的是異步事件處理程序,則為 void。
(4)方法通常包含至少一個(gè) await 表達(dá)式,該表達(dá)式標(biāo)記一個(gè)點(diǎn),在該點(diǎn)上,直到等待的異步操作完成方法才能繼續(xù)。?同時(shí),將方法掛起,并且控制權(quán)將返回到方法的調(diào)用方。
在異步方法中,可使用提供的關(guān)鍵字和類型來指示需要完成的操作,且編譯器會(huì)完成其余操作。?
?
四、異步方法的控制流(核心)
異步編程中最需弄清的是控制流,即如何從一個(gè)方法移動(dòng)到另一個(gè)方法, 請(qǐng)用一顆感恩的心來觀察下圖。
步驟解析:
①?事件處理程序調(diào)用并等待?AccessTheWebAsync?異步方法。
②?AccessTheWebAsync 創(chuàng)建?HttpClient?對(duì)象并調(diào)用它的?GetStringAsync?異步方法來下載網(wǎng)站內(nèi)容。
③ 假設(shè)?GetStringAsync?中發(fā)生了某種情況,該情況掛起了它的進(jìn)程。可能必須等待網(wǎng)站下載或一些其他阻塞的活動(dòng)。為避免阻塞資源,GetStringAsync?會(huì)將控制權(quán)出讓給其調(diào)用方?AccessTheWebAsync。GetStringAsync?返回?Task,其中 TResult 為字符串,并且?AccessTheWebAsync?將任務(wù)分配給?getStringTask?變量。該任務(wù)表示調(diào)用?GetStringAsync?的正在進(jìn)行的進(jìn)程,其中承諾當(dāng)工作完成時(shí)產(chǎn)生實(shí)際字符串值。
④?由于尚未等待?getStringTask,因此,AccessTheWebAsync?可以繼續(xù)執(zhí)行不依賴于?GetStringAsync?得出最終結(jié)果的其他任務(wù)。該任務(wù)由對(duì)同步方法?DoIndependentWork?的調(diào)用表示。
⑤?DoIndependentWork?是完成其工作并返回其調(diào)用方的同步方法。
⑥?AccessTheWebAsync?已完成工作,可以不受?getStringTask?的結(jié)果影響。?接下來,AccessTheWebAsync?需要計(jì)算并返回該下載字符串的長(zhǎng)度,但該方法僅在具有字符串時(shí)才能計(jì)算該值。因此,AccessTheWebAsync?使用一個(gè) await 運(yùn)算符來掛起其進(jìn)度,并把控制權(quán)交給調(diào)用?AccessTheWebAsync?的方法。AccessTheWebAsync?將?Task<int>?返回至調(diào)用方。?該任務(wù)表示對(duì)產(chǎn)生下載字符串長(zhǎng)度的整數(shù)結(jié)果的一個(gè)承諾。
【備注】如果?GetStringAsync(即?getStringTask)在?AccessTheWebAsync?等待前完成,則控制權(quán)會(huì)保留在?AccessTheWebAsync?中。?如果異步調(diào)用過程 (getStringTask) 已完成,并且 AccessTheWebSync 不必等待最終結(jié)果,則掛起然后返回到?AccessTheWebAsync,但這會(huì)造成成本的浪費(fèi)。
在調(diào)用方內(nèi)部(假設(shè)這是一個(gè)事件處理程序),處理模式將繼續(xù)。在等待結(jié)果前,調(diào)用方可以開展不依賴于?AccessTheWebAsync?結(jié)果的其他工作,否則就需等待片刻。事件處理程序等待?AccessTheWebAsync,而?AccessTheWebAsync?等待?GetStringAsync。
⑦?GetStringAsync?完成并生成一個(gè)字符串結(jié)果。?字符串結(jié)果不是通過你預(yù)期的方式調(diào)用?GetStringAsync?所返回的。(請(qǐng)記住,此方法已在步驟 3 中返回一個(gè)任務(wù)。)相反,字符串結(jié)果存儲(chǔ)在表示完成方法?getStringTask?的任務(wù)中。?await 運(yùn)算符從?getStringTask?中檢索結(jié)果。賦值語句將檢索到的結(jié)果賦給?urlContents。
⑧?當(dāng)?AccessTheWebAsync?具有字符串結(jié)果時(shí),該方法可以計(jì)算字符串長(zhǎng)度。然后,AccessTheWebAsync?工作也將完成,并且等待事件處理程序可繼續(xù)使用。?
?
你可以嘗試思考一下同步行為和異步行為之間的差異。當(dāng)其工作完成時(shí)(第 5 步)會(huì)返回一個(gè)同步方法,但當(dāng)其工作掛起時(shí)(第 3 步和第 6 步),異步方法會(huì)返回一個(gè)任務(wù)值。在異步方法最終完成其工作時(shí),任務(wù)會(huì)標(biāo)記為已完成,而結(jié)果(如果有)將存儲(chǔ)在任務(wù)中。
?
五、線程
異步方法旨在成為非阻塞操作。異步方法中的 await 表達(dá)式在等待的任務(wù)正在運(yùn)行時(shí)不會(huì)阻塞當(dāng)前線程。相反,表達(dá)式在繼續(xù)時(shí)注冊(cè)方法的其余部分并將控制權(quán)返回到異步方法的調(diào)用方。
async 和 await 關(guān)鍵字不會(huì)導(dǎo)致創(chuàng)建其他線程。因?yàn)楫惒椒椒ú粫?huì)在其自身線程上運(yùn)行,因此它不需要多線程。只有當(dāng)方法處于活動(dòng)狀態(tài)時(shí),該方法將在當(dāng)前同步上下文中運(yùn)行并使用線程上的時(shí)間。可以使用?Task.Run?將占用大量 CPU 的工作移到后臺(tái)線程,但是后臺(tái)線程不會(huì)幫助正在等待結(jié)果的進(jìn)程變?yōu)榭捎脿顟B(tài)。
對(duì)于異步編程而言,該基于異步的方法優(yōu)于幾乎每個(gè)用例中的現(xiàn)有方法。具體而言,此方法比?BackgroundWorker?更適用于 IO 綁定的操作,因?yàn)榇舜a更簡(jiǎn)單且無需防止搶先爭(zhēng)用條件。結(jié)合?Task.Run?使用時(shí),異步編程比?BackgroundWorker?更適用于 CPU 綁定的操作,因?yàn)楫惒骄幊虒⑦\(yùn)行代碼的協(xié)調(diào)細(xì)節(jié)與?Task.Run?傳輸至線程池的工作區(qū)分開來。
?
六、async 和 await
如果通過使用?async?修飾符指定某種方法為異步方法,則會(huì)出現(xiàn)下面兩種現(xiàn)象。
標(biāo)記的異步方法可以使用?await?來指定懸掛點(diǎn)。await 運(yùn)算符通知編譯器異步方法只有直到等待的異步過程完成才能繼續(xù)通過該點(diǎn)。同時(shí),控制權(quán)將返回至異步方法的調(diào)用方。
await 表達(dá)式中異步方法的掛起不能使該方法退出,并且?finally?塊不會(huì)運(yùn)行。
標(biāo)記的異步方法本身可以通過調(diào)用它的方法等待。
異步方法通常包含 await 運(yùn)算符的一個(gè)或多個(gè)匹配項(xiàng),但缺少 await 表達(dá)式不會(huì)導(dǎo)致編譯器錯(cuò)誤。如果異步方法未使用 await 運(yùn)算符標(biāo)記懸掛點(diǎn),則該方法將作為同步方法執(zhí)行,不管異步修飾符如何。編譯器將為此類方法發(fā)布一個(gè)警告。
?
七、返回類型和參數(shù)信息
在 .NET 中,異步方法通常返回?Task?或?Task<TResult>。在異步方法中,await 運(yùn)算符應(yīng)用于通過調(diào)用另一個(gè)異步方法返回的任務(wù)。
如果方法包含?指定類型?TResult?的操作數(shù)的?return?語句,則將?Task<TResult>?指定為返回類型。
如果方法不含任何 return 語句或包含不返回操作數(shù)的 return 語句,則將?Task?用作返回類型。
下面的示例演示如何聲明并調(diào)用可返回?Task?或?Task<TResult> 的方法。
static async Task<Guid> Method1Async() //Task<Guid>
? ? ? ? {
? ? ? ? ? ? var result = Guid.NewGuid();
? ? ? ? ? ? await Task.Delay(1);
? ? ? ? ? ? //這里返回一個(gè) Guid 的類型
? ? ? ? ? ? return result;
? ? ? ? }
? ? ? ? static async Task Method2Async() //Task
? ? ? ? {
? ? ? ? ? ? //Do...
? ? ? ? ? ? await Task.Delay(1);
? ? ? ? ? ? //Do...
? ? ? ? ? ? //這里沒有 return 語句
? ? ?? ? }
//調(diào)用 Method1Async
? ? ? ? ? ? //方式一
? ? ? ? ? ? Task<Guid> t1 = Method1Async();
? ? ? ? ? ? Guid guid1 = t1.Result;
? ? ? ? ? ? //方式二
? ? ? ? ? ? Guid guid2 = await Method1Async();
? ? ? ? ? ? //調(diào)用 Method2Async
? ? ? ? ? ? //方式一
? ? ? ? ? ? Task t2 = Method2Async();
? ? ? ? ? ? await t2;
? ? ? ? ? ? //方式二
? ? ? ? ? ? await Method2Async();
每個(gè)返回的任務(wù)表示正在進(jìn)行的工作。任務(wù)可封裝有關(guān)異步進(jìn)程狀態(tài)的信息,如果未成功,則最后會(huì)封裝來自進(jìn)程的最終結(jié)果或進(jìn)程引發(fā)的異常。
異步方法還可以是具有?void?返回類型。該返回類型主要用于定義需要?void?返回類型的事件處理程序。異步事件處理程序通常用作異步程序的起始點(diǎn)。
無法等待具有?void?返回類型的異步方法,并且一個(gè) void 返回值的調(diào)用方無法捕獲該方法引發(fā)的任何異常。
異步方法無法聲明 C# 中的?ref?或?out?參數(shù),但此方法可以調(diào)用具有此類參數(shù)的方法。
?
八、命名的約定
根據(jù)約定,將“Async”追加到具有?async?修飾符的方法名稱。
如果某一約定中的事件、基類或接口協(xié)定建議其他名稱,則可以忽略此約定。例如,你不應(yīng)重命名常用事件處理程序,例如?btnOpen_Click。
相關(guān)文章:?
Async/Await 異步編程中的最佳做法
[C#]async和await刨根問底
剖析異步編程語法糖: async和await
Async/Await 異步編程中的最佳做法
原文地址:http://www.cnblogs.com/liqingwen/p/5922573.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的利用 async amp; await 的异步编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core 之 Ident
- 下一篇: 缓存在大型网站架构中的应用