通過前面的文章,已經學習了怎么使用線程,怎么使用線程同步,怎么使用線程池,怎么使用任務并行庫。盡管通過上面的學習,對于線程的使用越來越簡單。有沒有更簡單的方法呢。
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>。
Await只能使用在有async標志的方法內部。在async標記的方法內部最少要有一個await,當然,如果一個也沒有,編譯也不會報錯,但是會有編譯警告。如下圖。
上面的代碼在執行完await調用的代碼之行后該方法會直接返回。如果同步執行,執行線程會阻塞2秒之后返回結果,本示例里在執行完await操作后,立即將工作線程放回線程池中,我們會異步等待。2秒后,我們會從線程池中取得工作線程并繼續運行其中剩余的異步方法。這就允許我們在等待的2秒的時間里可以重用線程池中的線程,這對提高應用程序的可伸縮性非常重要。通過使用async與await我們擁有了線性的程序控制流程,但是執行過程卻是異步的。
一、 使用await獲取異步操作結果
本示例是學習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方法同,因為如果代碼寫的不好,很容易導致死鎖。
二、 在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 表達式執行后的結果。
文章列表