Task異步編程中,可以實現在等待耗時任務的同時,執行不依賴於該耗時任務結果的其他同步任務,提高效率。
1、Task異步編程方法簽名及返回值:
a) 簽名有async 修飾符
b) 方法名以 Async 結尾(良好的編碼習慣)
根據約定,將“Async”追加到具有 async 修飾符的方法名稱。如果某一約定中的事件、基類或接口協定建議其他名稱,則可以忽略此約定。例如,你不應重命名常用事件處理程序,例如 btnOpen_Click。
c) 返回類型如下:
- 如果方法有操作數為TResult 類型的返回語句,則為 Task<TResult>;
- 如果你的方法沒有返回語句或具有沒有操作數的返回語句,則為 Task;
- 如果你編寫的是異步事件處理程序,則為 void。
d) 方法通常包含至少一個 await 表達式,該表達式標記一個點,在該點上,直到等待的異步操作完成,方法才能繼續。 同時,將方法掛起,並且控制權將返回到方法的調用方。
舉例如下:
private static async Task<int> OperateAsync()
2、 舉例分析:
static void Main(string[] args) { Test1.DoClickAsync(); Console.WriteLine("不等待異步方法,直接執行"); Console.ReadKey(); }
public class Test1 { public static async void DoClickAsync() { Task<int> result = OperateAsync(); Console.WriteLine("從OperateAsync方法中控制權限返回調用方DoClickAsync"); int resultValue = await result; Console.WriteLine("耗時操作結果" + resultValue.ToString()); } private static async Task<int> OperateAsync() { HttpClient client = new HttpClient(); //執行異步方法 GetStringAsync Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com"); //由於下面的非異步方法不依賴上面異步方法結果,因此可以先執行,假設在這里執行一些非異步的操作 DoIndependentWork(); //等待操作掛起方法 OperateAsync,直到 getStringTask 完成,OperateAsync 方法才會繼續執行 //同時,控制將返回到 OperateAsync 方法的調用方,直到 getStringTask 完成后,將在這里恢復控制。 //然后從 getStringTask 拿到字符串結果 string urlContents = await getStringTask; Console.WriteLine("方法OperateAsync"); //返回字符串的長度(int 類型) return urlContents.Length; } private static void DoIndependentWork() { for (int i = 0; i < 5; i++) { Console.WriteLine("主線程執行內容" + i); Thread.Sleep(1000); } } }
3、步驟分析:
- Main主函數,調用並等待異步方法DoClickAsync;
- DoClickAsync方法調用並等待異步方法OperateAsync;
- 異步方法OperateAsync調用HttpClient對象並調用它的GetStringAsync異步方法來下載網站內容
- 假設GetStringAsync中發生了某種情況,該情況掛起了它的進程。可能必須等待網站下載或一些其他阻塞的活動。為避免阻塞資源,GetStringAsync 會將控制權出讓給其調用方 OperateAsync。GetStringAsync返回Task,其中TResult為字符串,並且OperateAsync將任務分配給getStringTask變量。該任務表示調用GetStringAsync的正在進行的進程,其中承諾當工作完成時產生實際字符串值。
- 由於尚未等待getStringTask,因此,OperateAsync可以繼續執行不依賴於 GetStringAsync得出最終結果的其他任務。該任務由對同步方法 DoIndependentWork 的調用表示。
- DoIndependentWork 是完成其工作並返回其調用方的同步方法。
- OperateAsync 已完成工作,可以不受 getStringTask 的結果影響。 接下來,OperateAsync 需要計算並返回該下載字符串的長度,但該方法僅在具有字符串時才能計算該值。因此,OperateAsync 使用一個 await 運算符來掛起其進度,並把控制權交給調用 OperateAsync 的方法。OperateAsync將 Task<int> 返回至調用方。該任務表示對產生下載字符串長度的整數結果的一個承諾。
【備注】
(1)如果 GetStringAsync(即 getStringTask)在 OperateAsync 等待前完成,則控制權會保留在 OperateAsync 中。 如果異步調用過程 (getStringTask) 已完成,並且OperateAsync不必等待最終結果,則掛起然后返回到 OperateAsync,但這會造成成本的浪費。
(2)在調用方內部(DoClickAsync),處理模式將繼續。在等待結果前,調用方可以開展不依賴於OperateAsync結果的其他工作,否則就需等待片刻。DoClickAsync等待 OperateAsync,而 OperateAsync 等待 GetStringAsync。
- GetStringAsync 完成並生成一個字符串結果。 字符串結果不是通過你預期的方式調用 GetStringAsync 所返回的。(請記住,此方法已在前面步驟中返回一個任務。)相反,字符串結果存儲在表示完成方法 getStringTask 的任務中。 await 運算符從 getStringTask 中檢索結果。賦值語句將檢索到的結果賦給 urlContents。
- 當OperateAsync具有字符串結果時,該方法可以計算字符串長度。然后,OperateAsync 工作也將完成,並且等待程序DoClickAsync可繼續使用。
4、同步行為和異步行為差異:
- 同步行為,當其工作完成時會返回一個同步方法;
- 異步行為,當其工作掛起時,異步方法會返回一個任務值。在異步方法最終完成其工作時,任務會標記為已完成,而結果(如果有)將存儲在任務中。
5、詳談返回類型:
在.NET 中,異步方法通常返回 Task 或 Task<TResult>。在異步方法中,await運算符應用於通過調用另一個異步方法返回的任務。
1、如果方法包含指定類型 TResult 的操作數的return語句,則將 Task<TResult> 指定為返回類型。
2、如果方法不含任何 return 語句或包含不返回操作數的 return 語句,則將Task用作返回類型。
【備注】
每個返回的任務表示正在進行的工作。任務可封裝有關異步進程狀態的信息,如果未成功,則最后會封裝來自進程的最終結果或進程引發的異常。
3、異步方法還可以是具有void返回類型。該返回類型主要用於定義需要void返回類型的事件處理程序。異步事件處理程序通常用作異步程序的起始點。無法等待具有void返回類型的異步方法,並且一個void返回值的調用方無法捕獲該方法引發的任何異常。
下面的示例演示如何聲明並調用可返回 Task 或 Task<TResult> 的方法。
static void Main(string[] args) { DoMethodAsync(); Console.ReadKey(); } private static async void DoMethodAsync() { //調用Method1Async /*方式一*/ Task<Guid> t1 = Method1Async(); Guid guid1 = t1.Result; Console.WriteLine("調用Method1Async方式一操作結果" + guid1.ToString()); /*方式二*/ Guid guid2 = await Method1Async(); Console.WriteLine("調用Method1Async方式二操作結果" + guid2.ToString()); //調用Method2Async /*方式一*/ Task t2 = Method2Async(); await t2; /*方式二*/ await Method2Async(); } //返回Task<TResult>類型 static async Task<Guid> Method1Async() //Task<Guid> { Console.WriteLine("調用Method1Async"); var result = Guid.NewGuid(); await Task.Delay(10000); //這里返回一個 Guid 的類型 return result; } //返回Task類型 static async Task Method2Async() //Task { Console.WriteLine("調用Method2Async"); //Do... await Task.Delay(10000); //Do... //這里沒有 return 語句 }
6、總結:
1、標記的異步方法(通過使用 async 修飾符)可以使用 await 來指定懸掛點。await 運算符通知編譯器,異步方法只有直到等待的異步過程完成才能繼續通過該點。同時,控制權將返回至異步方法的調用方。
2、異步方法通常包含 await 運算符的一個或多個匹配項,但缺少 await 表達式不會導致編譯器錯誤。如果異步方法未使用 await 運算符標記懸掛點,則該方法將作為同步方法執行,不管異步修飾符(async)如何。編譯器將為此類方法發布一個警告。
7、參考:
http://www.cnblogs.com/liqingwen/p/5922573.html