[C#] 異步編程,
剖析異步方法
序
這是上篇《開始接觸 async/await 異步編程》(入門)的第二章內容,主要是深入了解異步方法,建議大家先看入門篇,很短。
本文要求了解委托的使用。
目錄
介紹異步方法
異步方法:在執行完成前立即返回調用方法,在調用方法繼續執行的過程中完成任務。
語法分析:
(1)關鍵字:方法頭使用 async 修飾。
(2)要求:包含 N(N>0) 個 await 表達式(不存在 await 表達式的話 IDE 會發出警告),表示需要異步執行的任務。【備注】感謝 czcz1024 的修正與補充:沒有的話,就和普通方法一樣執行了。
(3)返回類型:只能返回 3 種類型(void、Task 和 Task<T>)。Task 和 Task<T> 標識返回的對象會在將來完成工作,表示調用方法和異步方法可以繼續執行。
(4)參數:數量不限。但不能使用 out 和 ref 關鍵字。
(5)命名約定:方法後綴名應以 Async 結尾。
(6)其它:匿名方法和 Lambda 表達式也可以作為異步對象;async 是一個上下文關鍵字;關鍵字 async 必須在返回類型前。
![]()
關於 async 關鍵字:
①在返回類型之前包含 async 關鍵字
②它只是標識該方法包含一個或多個 await 表達式,即,它本身不創建異步操作。
③它是上下文關鍵字,即可作為變量名。
現在先來著重分析一下這三種返回值類型: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 }
Program.cs

![]()
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 }
View Code
![]()
(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 }
Program.cs

![]()
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 }
View Code

圖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 }
Program.cs

![]()
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 }
Calculator.cs

圖6

圖7
一、控制流
異步方法的結構可拆分成三個不同的區域:
(1)表達式之前的部分:從方法頭到第一個 await 表達式之間的所有代碼。
(2)await 表達式:將被異步執行的代碼。
(3)表達式之後的部分:await 表達式的後續部分。
![]()
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 }
View Code
圖2-1
圖2-2
上面 4 個 Task.Run() 都是采用了 Task Run(Func<TReturn> func) 形式。
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 }
View Code
三、取消異步操作
四、異常處理
五、在調用方法中同步地等待任務
六、在異步方法中異步地等待任務
七、Task.Delay 方法
小結
傳送門
入門:《開始接觸 async/await 異步編程》
--這是預覽版本,最終整理完校對後將放上首頁,不便之處請諒解--