接上文 多線程編程學習筆記——任務並行庫(一)html
接上文 多線程編程學習筆記——任務並行庫(二)編程
接上文 多線程編程學習筆記——任務並行庫(三)c#
接上文 多線程編程學習筆記——任務並行庫(四)多線程
經過前面的文章,已經學習了怎麼使用線程,怎麼使用線程同步,怎麼使用線程池,怎麼使用任務並行庫。儘管經過上面的學習,對於線程的使用愈來愈簡單。有沒有更簡單的方法呢。asp.net
C# 5.0以後,微軟在c#語言中添加了兩個關鍵字async與await,這是在TPL上面的更高一級的抽象,真正簡化了異步編程的編程方式,從而有助於咱們編寫出真正健壯少bug的異步應用程序。下面我先來看一個最簡單的示例。異步
async Task<string> AsyncHello() { await Task.Delay(TimeSpan.FromSeconds(2)); Return 「 Hello world」; }
使用async標記異步函數,建議返回async Task<T>。async
Await只能使用在有async標誌的方法內部。在async標記的方法內部最少要有一個await,固然,若是一個也沒有,編譯也不會報錯,可是會有編譯警告。以下圖。異步編程
上面的代碼在執行完await調用的代碼之行後該方法會直接返回。若是同步執行,執行線程會阻塞2秒以後返回結果,本示例裏在執行完await操做後,當即將工做線程放回線程池中,咱們會異步等待。2秒後,咱們會從線程池中取得工做線程並繼續運行其中剩餘的異步方法。這就容許咱們在等待的2秒的時間裏能夠重用線程池中的線程,這對提升應用程序的可伸縮性很是重要。經過使用async與await咱們擁有了線性的程序控制流程,可是執行過程倒是異步的。函數
1、 使用await獲取異步操做結果post
本示例是學習await如何獲取異步操做結果。同時會與TPL進行比較。
1.示例代碼以下。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; namespace ThreadAsyncDemo { class Program { static void Main(string[] args) { Task t = AsyncWithTPL(); t.Wait(); t = AsyncWithAwait(); t.Wait(); Console.Read(); } static Task AsyncWithTPL() { Task<string> task1 = GetInfoAsync("Task 1"); Task task2 = task1.ContinueWith(task => Console.WriteLine(task1.Result), TaskContinuationOptions.NotOnFaulted); Task task3 = task1.ContinueWith(task => Console.WriteLine(task1.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted); return Task.WhenAny(task2, task1); } async static Task AsyncWithAwait() { try { string result = await GetInfoAsync("Task 4"); Console.WriteLine(result); } catch (Exception ex) { Console.WriteLine(ex.Message); } } async static Task<string> GetInfoAsync(string name) { await Task.Delay(TimeSpan.FromSeconds(2)); //throw new Exception("拋出異常信息!"); return string.Format(" Task {0} 正在運行在線程 ID={1}上。這個工做線程是不是線程池中的線程:{2}", name,
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); } } }
2.程序運行的結果以下圖。
程序同時運行了兩個異步操做。其中一個是標準的TPL代碼,另外一個使用了async與await兩個關鍵字。AsyncWithTPL啓動了一個任務,運行兩秒以後返回關於工做線程信息的字符串。而後咱們定義了一個後續操做,用於在異步操做完成後打印出操做結果,還有另外一個後續操做,用於萬一有錯誤時,打印出異常信息。最終返回了一個表明其中一個後續操做任務的任務,並等等其在主函數中完成。
在asyncWithAwait方法中,咱們對任務使用await並獲得了相同 的結果。這和編寫普通的同步代碼的風格同樣,即咱們獲取了任務的結果,打印了出來,若是任務完成時帶有錯誤則捕獲異常。關鍵不一樣的是這其實是一個異步操做。使用await後,c#當即建立了一個任務,其中一個有後續操做任務,包含了await操做符後面的全部剩餘代碼。這個新任務也處理了異常。而後這個任務返回 到主方法並等待共完成 。
所以能夠看出程序中的兩段代碼在概念上是相同的,使用await由編譯 器隱式地處理了異步代碼。
3. 咱們把上面註釋的拋出異常的代碼,取消註釋,而後運行程序。獲得以下圖的結果。
注:在gui與asp.net之類的環境 中不推薦 使用task.wait和taskResult方法同,由於若是代碼寫的很差,很容易致使死鎖。
2、 在Lambda表達式中使用await操做符
本示例學習如何在lambda表達式中使用await。將學習如何編寫一個使用了await的匿名方法,而且獲取異步執行該方法的結果。
1.示例代碼以下。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; namespace ThreadAsyncDemo { class Program { static void Main(string[] args) { Task t = AsyncProcess(); t.Wait(); Console.Read(); } async static Task AsyncProcess() { Func<String, Task<string>> asyncLambda = async name => { await Task.Delay(TimeSpan.FromSeconds(2)); return string.Format(" Task {0} 正在運行在線程 ID={1}上。這個工做線程是線程池的線程:{2}" ,name,
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); }; string result = await asyncLambda("async lambda"); Console.WriteLine(result); } } }
2.程序運行結果,以下圖。
首先不能在main方法中使用async,咱們將異步函數移到了asyncProcess中,而後使用async關鍵字聲明瞭一個lambda表達式。因爲 任何lambda表達式的類型都不能經過lambda自身來推斷,因此不得不顯示地指定類型爲一字符串,並返回一個Task<string>對象 。
而後,咱們定義 了lambda表達式體,這個方法雖然定義返回的是一個Task<string>對象 ,但實際上返回的是字符串,卻沒有編譯錯誤。這是由於c#編譯器自動 產生了一個任務並返回給咱們。
最後一步就是打印出lambda 表達式執行後的結果。