這是上篇《走進異步編程的世界 - 開始接觸 async/await 異步編程》(入門)的第二章內容,主要是與你們共同深刻探討下異步方法。html
本文要求瞭解委託的使用。編程
介紹異步方法異步
控制流async
await 表達式ide
How 取消異步操做異步編程
關於 async 關鍵字:post
①在返回類型以前包含 async 關鍵字ui
②它只是標識該方法包含一個或多個 await 表達式,即,它自己不建立異步操做。url
③它是上下文關鍵字,便可做爲變量名。spa
如今先來簡單分析一下這三種返回值類型:void、Task 和 Task<T>
(1)Task<T>:調用方法要從調用中獲取一個 T 類型的值,異步方法的返回類型就必須是Task<T>。調用方法從 Task 的 Result 屬性獲取的就是 T 類型的值。
1 private static void Main(string[] args) 2 { 3 Task<int> t = Calculator.AddAsync(1, 2); 4 5 //一直在幹活 6 7 Console.WriteLine($"result: {t.Result}"); 8 9 Console.Read(); 10 }
1 internal class Calculator 2 { 3 private static int Add(int n, int m) 4 { 5 return n + m; 6 } 7 8 public static async Task<int> AddAsync(int n, int m) 9 { 10 int val = await Task.Run(() => Add(n, m)); 11 12 return val; 13 } 14 }
圖2
圖3
(2)Task:調用方法不須要從異步方法中取返回值,可是但願檢查異步方法的狀態,那麼能夠選擇能夠返回 Task 類型的對象。不過,就算異步方法中包含 return 語句,也不會返回任何東西。
1 private static void Main(string[] args) 2 { 3 Task t = Calculator.AddAsync(1, 2); 4 5 //一直在幹活 6 7 t.Wait(); 8 Console.WriteLine("AddAsync 方法執行完成"); 9 10 Console.Read(); 11 }
1 internal class Calculator 2 { 3 private static int Add(int n, int m) 4 { 5 return n + m; 6 } 7 8 public static async Task AddAsync(int n, int m) 9 { 10 int val = await Task.Run(() => Add(n, m)); 11 Console.WriteLine($"Result: {val}"); 12 } 13 }
圖4
圖5
(3)void:調用方法執行異步方法,但又不須要作進一步的交互。
1 private static void Main(string[] args) 2 { 3 Calculator.AddAsync(1, 2); 4 5 //一直在幹活 6 7 Thread.Sleep(1000); //掛起1秒鐘 8 Console.WriteLine("AddAsync 方法執行完成"); 9 10 Console.Read(); 11 }
1 internal class Calculator 2 { 3 private static int Add(int n, int m) 4 { 5 return n + m; 6 } 7 8 public static async void AddAsync(int n, int m) 9 { 10 int val = await Task.Run(() => Add(n, m)); 11 Console.WriteLine($"Result: {val}"); 12 } 13 }
圖6
圖7
await 表達式指定了一個異步執行的任務。默認狀況,該任務在當前線程異步執行。
每個任務就是一個 awaitable 類的實例。awaitable 類型指包含 GetAwaiter() 方法的類型。
實際上,你並不須要構建本身的 awaitable,通常只須要使用 Task 類,它就是 awaitable。
最簡單的方式是在方法中使用 Task.Run() 來建立一個 Task。【注意】它是在不一樣的線程上執行方法。
讓咱們一塊兒來看看示例。
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 var t = Do.GetGuidAsync(); 6 t.Wait(); 7 8 Console.Read(); 9 } 10 11 12 private class Do 13 { 14 /// <summary> 15 /// 獲取 Guid 16 /// </summary> 17 /// <returns></returns> 18 private static Guid GetGuid() //與Func<Guid> 兼容 19 { 20 return Guid.NewGuid(); 21 } 22 23 /// <summary> 24 /// 異步獲取 Guid 25 /// </summary> 26 /// <returns></returns> 27 public static async Task GetGuidAsync() 28 { 29 var myFunc = new Func<Guid>(GetGuid); 30 var t1 = await Task.Run(myFunc); 31 32 var t2 = await Task.Run(new Func<Guid>(GetGuid)); 33 34 var t3 = await Task.Run(() => GetGuid()); 35 36 var t4 = await Task.Run(() => Guid.NewGuid()); 37 38 Console.WriteLine($"t1: {t1}"); 39 Console.WriteLine($"t2: {t2}"); 40 Console.WriteLine($"t3: {t3}"); 41 Console.WriteLine($"t4: {t4}"); 42 } 43 } 44 }
圖2-1
圖2-2
上面 4 個 Task.Run() 都是採用了 Task Run(Func<TReturn> func) 形式來直接或間接調用 Guid.NewGuid()。
Task.Run() 支持 4 中不一樣的委託類型所表示的方法:Action、Func<TResult>、Func<Task> 和 Func<Task<TResult>>
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 var t = Do.GetGuidAsync(); 6 t.Wait(); 7 8 Console.Read(); 9 } 10 11 private class Do 12 { 13 public static async Task GetGuidAsync() 14 { 15 await Task.Run(() => { Console.WriteLine(Guid.NewGuid()); }); //Action 16 17 Console.WriteLine(await Task.Run(() => Guid.NewGuid())); //Func<TResult> 18 19 await Task.Run(() => Task.Run(() => { Console.WriteLine(Guid.NewGuid()); })); //Func<Task> 20 21 Console.WriteLine(await Task.Run(() => Task.Run(() => Guid.NewGuid()))); //Func<Task<TResult>> 22 } 23 } 24 }
圖2-3 Task.Run() 方法的重載
CancellationToken 和 CancellationTokenSource 這兩個類容許你終止執行異步方法。
(1)CancellationToken 對象包含任務是否被取消的信息;若是該對象的屬性 IsCancellationRequested 爲 true,任務需中止操做並返回;該對象操做是不可逆的,且只能使用(修改)一次,即該對象內的 IsCancellationRequested 屬性被設置後,就不能改動。
(2)CancellationTokenSource 可建立 CancellationToken 對象,調用 CancellationTokenSource 對象的 Cancel 方法,會使該對象的 CancellationToken 屬性 IsCancellationRequested 設置爲 true。
【注意】調用 CancellationTokenSource 對象的 Cancel 方法,並不會執行取消操做,而是會將該對象的 CancellationToken 屬性 IsCancellationRequested 設置爲 true。
示例
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 CancellationTokenSource source = new CancellationTokenSource(); 6 CancellationToken token = source.Token; 7 8 var t = Do.ExecuteAsync(token); 9 10 //Thread.Sleep(3000); //掛起 3 秒 11 //source.Cancel(); //傳達取消請求 12 13 t.Wait(token); //等待任務執行完成 14 Console.WriteLine($"{nameof(token.IsCancellationRequested)}: {token.IsCancellationRequested}"); 15 16 Console.Read(); 17 } 18 19 20 } 21 22 internal class Do 23 { 24 /// <summary> 25 /// 異步執行 26 /// </summary> 27 /// <param name="token"></param> 28 /// <returns></returns> 29 public static async Task ExecuteAsync(CancellationToken token) 30 { 31 if (token.IsCancellationRequested) 32 { 33 return; 34 } 35 36 await Task.Run(() => CircleOutput(token), token); 37 } 38 39 /// <summary> 40 /// 循環輸出 41 /// </summary> 42 /// <param name="token"></param> 43 private static void CircleOutput(CancellationToken token) 44 { 45 Console.WriteLine($"{nameof(CircleOutput)} 方法開始調用:"); 46 47 const int num = 5; 48 for (var i = 0; i < num; i++) 49 { 50 if (token.IsCancellationRequested) //監控 CancellationToken 51 { 52 return; 53 } 54 55 Console.WriteLine($"{i + 1}/{num} 完成"); 56 Thread.Sleep(1000); 57 } 58 } 59 }
圖3-1
圖3-2 註釋兩行代碼
圖3-3:圖3-1和圖3-2的執行結果(註釋兩行代碼)
上圖是不調用 Cancel() 方法的結果圖,不會取消任務的執行。
下圖在 3 秒後調用 Cancel() 方法取消任務的執行:
圖3-4:去掉註釋
圖3-5:圖3-1和圖3-4的執行結果(去掉註釋)
補充篇:《走進異步編程的世界 - 剖析異步方法(下)》
GUI 篇:《走進異步編程的世界 - 在 GUI 中執行異步操做》
原文連接:http://www.cnblogs.com/liqingwen/p/5844095.html
【參考】《Illustrated C# 2012》