c#多線程(二)——同步和異步
1、什麽是異步
如果一個程序調用某個方法,等待其執行所有處理後才繼續執行,我們稱這樣的方法是同步的。
如果一個程序調用某個方法,在該方法處理完成之前就返回到調用方法,則這個方法是異步的。
異步的好處在於非阻塞,因此我們把一些不需要立即使用結果、較耗時的任務設為異步時,可以提高程序的運行效率。
2、異步方法
2.1 異步方法簡單使用
在C#5.0中出現的async/await可以方便的創建並調用異步方法,async/await特性包括三個部分
1.調用方法:就是調用異步方法的方法
2.async方法:異步方法,被調用時立即返回到調用方法
3.await表達式:在異步方法內部,表示異步執行的任務。一個異步方法可以包含多個await表達式,即一個異步方法可以有多個異步執行的任務。
一個簡單的例子:
1 static void Main(string[] args) 2 { 3 //調用方法是Main,異步方法立即返回 4 //Task<int>占位符表示把任務放在計劃中,任務最終返回一個int類型的結果 5 Task<int> value = DoAsyncStuff.CalcSumAsync(5, 6); 6 //如果執行到這裏異步方法還沒有完成,則阻塞並等待 7 Console.WriteLine("result:{0}", value.Result); 8 } 9 } 10 static class DoAsyncStuff 11 { 12 //異步求和,方法簽名用async修飾 13 //異步方法的返回值有三種: 14 //Task<T> 有返回值,返回值的類型為T 15 //Task 無返回值,但是需要監控執行狀態 16 //void 只是調用一下就不管了 17 public static async Task<int> CalcSumAsync(inta, int b) 18 { 19 //await表達式,表示異步操作的任務 Task.Run()創建一個任務 20 int sum = await Task.Run(() => GetSum(a, b)); 21 return sum; 22 } 23 private static int GetSum(int a, int b) 24 { 25 return a + b; 26 } 27 }
異步方法使用async方法修飾符,方法中有一個或多個await表達式,返回值只有三種
①Task<T>:如果調用方法從調用中獲取一個T類型的值,則異步方法的返回值為Task<T>
②Task:如果調用方法不需要從異步方法中獲取值,但是需要檢查異步方法的狀態,返回值可以設置為Task
③void:如果調用方法只是調用一下異步方法就什麽都不管了,則返回值設為void
2.2 異步方法的控制流
調用方法調用異步方法時用兩個控制流,一是調用方法中,一是異步方法中。
首先調用方法調用異步方法同步地執行異步方法中的代碼,直到遇到第一個await表達式(這裏控制交給了異步方法)
如果異步方法返回值是Task或Task<T>,異步方法立即創建一個空閑任務返回給調用方法,調用方法不等待異步任務完成(非阻塞)就執行自己內部的代碼(這裏控制交給調用方法)
當第一個await表達式中任務完成後,異步方法繼續執行其內部的代碼(控制又交給異步方法)
在遇到下一個await表達式時,重復以上操作,直到遇到return或者執行到異步方法末尾,註意異步方法在結束時並沒有返回一個真正的值,它只是退出了。
2.3 await表達式
2.3.1 await 的使用方法
await表達式指定異步操作的任務,我們可以設置task任務,最常用的方式是:
//1、使用Task.Run新建一個任務 await Task.Run(Func/Action) //2、執行一個方法,該方法必須是異步方法 await AsyncFunc
下邊是一個異步求和求差的例子:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Task<int> tsum = AsyncStuff.GetSumAsync(); 6 Task<int> tsub = AsyncStuff.GetSubAsync(); 7 //1、等待,直到tsum完成 8 tsum.Wait(); 9 Task[] tasks = { tsum, tsub }; 10 //2、等待,直到tasks都完成 11 Task.WaitAll(tasks); 12 //3、等待,直到tasks中有一個完成 13 Task.WaitAny(tasks); 14 15 //查看tasks的狀態 16 Console.WriteLine(tsum.Status); 17 Console.WriteLine(tsub.Status); 18 //記錄結果 19 Console.WriteLine(tsum.Result); 20 Console.WriteLine(tsub.Result); 21 Console.ReadKey(); 22 } 23 } 24 class AsyncStuff 25 { 26 //異步求和 27 public static async Task<int> GetSumAsync() 28 { 29 int sum = 0; 30 //Task.Run(Func/Action)創建一個任務 31 await Task.Run(() => 32 { 33 for (int i = 1; i < 1000000; i++) 34 { 35 sum += i; 36 } 37 }); 38 return sum; 39 } 40 //異步求差 41 public static async Task<int> GetSubAsync() 42 { 43 int sub = 0; 44 await Task.Run(() => 45 { 46 for (int i = 0; i < 1000000; i++) 47 { 48 sub -= i; 49 } 50 }); 51 return sub; 52 } 53 }
在例子中我們使用Wait、WaitAny、WaitAll實現在調用方法中同步等待任務完成,我們也可以使用WhenAny/WhenAll實現在異步方法中異步地等待任務,這裏不再舉例。
Task.Delay()方法創建一個Task對象,該對象將暫停異步任務的處理,並在一定時間之後完成,與Thread.Sleep不同,Task.Delay不會阻塞線程,線程可以繼續處理其他工作。
一個Task.Delay例子:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Simple sp = new Simple(); 6 sp.DoRun(); 7 Console.ReadKey(); 8 } 9 } 10 11 class Simple 12 { 13 Stopwatch sw = new Stopwatch(); 14 public void DoRun() 15 { 16 sw.Start(); 17 Console.WriteLine("Caller:Before call—— {0}",sw.ElapsedMilliseconds); 18 ShowDelyAsync(); 19 Console.WriteLine("Caller:After call—— {0}", sw.ElapsedMilliseconds); 20 } 21 public async void ShowDelyAsync() 22 { 23 Console.WriteLine("Before Delay—— {0}", sw.ElapsedMilliseconds); 24 await Task.Delay(1000); 25 Console.WriteLine("After Delay—— {0}", sw.ElapsedMilliseconds); 26 } 27 }
c#多線程(二)——同步和異步