C#中的async/await
async方法只能有三种返回值:void, Task, Task<T>。
async void只能直接调用,与调用方并行执行。
async Task可以直接调用,也可以await调用,直接调用是并行执行,await调用会等待执行完。
async Task<T>与async Task类似,只是通过await方式调用可以获得T类型的返回值。
下面通过代码做了一些实验。
class Program { static void Main(string[] args) { Console.WriteLine(DateTime.Now.ToString() + " : " + "程序启动"); Test8(); Console.WriteLine(DateTime.Now.ToString() + " : " + "程序结束"); for (int i = 0; i < 10; i++) { Console.WriteLine(DateTime.Now.ToString() + " : Ticks"); Thread.Sleep(1000); } Console.ReadLine(); } private static void TaskFunc(int n) { Console.WriteLine(DateTime.Now.ToString() + " : " + "Begin Call TaskFunc(" + n + ")"); Thread.Sleep(2000); Console.WriteLine(DateTime.Now.ToString() + " : " + "End Call TaskFunc(" + n + ")"); } /*********************************************************** 2018/9/25 10:51:34 : 程序启动 2018/9/25 10:51:34 : Begin Call TaskFunc(1) 2018/9/25 10:51:36 : End Call TaskFunc(1) 2018/9/25 10:51:36 : Begin Call TaskFunc(2) 2018/9/25 10:51:38 : End Call TaskFunc(2) :同步调用,两个任务依次执行,总耗时4秒 2018/9/25 10:51:38 : 程序结束 ***********************************************************/ private static void Test1() { TaskFunc(1); TaskFunc(2); } /*********************************************************** 2018/9/25 11:12:13 : 程序启动 2018/9/25 11:12:13 : Begin Call TaskFunc(1) 2018/9/25 11:12:13 : Begin Call TaskFunc(2) 2018/9/25 11:12:15 : End Call TaskFunc(1) 2018/9/25 11:12:15 : End Call TaskFunc(2) 2018/9/25 11:12:15 : 程序结束 :主线程阻塞直到全部任务执行完毕 ***********************************************************/ // 并行运行的只有两个TaskFunc,主线程是阻塞的 private static void Test2() { int[] a = new int[2] { 1, 2 }; Parallel.ForEach(a, n => { TaskFunc(n); }); } /*********************************************************** 2018/9/25 11:03:54 : 程序启动 2018/9/25 11:03:54 : Begin Call TaskFunc(1) 2018/9/25 11:03:54 : Begin Call TaskFunc(2) 2018/9/25 11:03:56 : End Call TaskFunc(1) 2018/9/25 11:03:56 : End Call TaskFunc(2) :两个任务同时开始同时结束,总耗时2秒 2018/9/25 11:03:56 : 程序结束 :Task.WaitAll阻塞了主线程 ***********************************************************/ // 并行运行的只有两个TaskFunc,主线程是阻塞的,与Test2相同 // 去掉Task.WaitAll,可以实现主线程、两个TaskFunc并行,但无法在两个TaskFunc完成后做些事情 private static void Test3() { Task t1 = Task.Run(() => { TaskFunc(1); }); Task t2 = Task.Run(() => { TaskFunc(2); }); Task.WaitAll(t1, t2); } /*********************************************************** 2018/9/25 11:01:32 : 程序启动 2018/9/25 11:01:32 : Begin Call TaskFunc(1) 2018/9/25 11:01:32 : Begin Call TaskFunc(2) 2018/9/25 11:01:34 : End Call TaskFunc(1) 2018/9/25 11:01:34 : End Call TaskFunc(2) :两个任务同时开始同时结束,总耗时2秒 2018/9/25 11:01:34 : 程序结束 :EndInvoke阻塞了主线程 ***********************************************************/ // 并行运行的只有两个TaskFunc,主线程是阻塞的,与Test2,Test3相同 private static void Test4() { Action a1 = () => { TaskFunc(1); }; Action a2 = () => { TaskFunc(2); }; IAsyncResult iar1 = a1.BeginInvoke(null, null); IAsyncResult iar2 = a2.BeginInvoke(null, null); a1.EndInvoke(iar1); a2.EndInvoke(iar2); } /*********************************************************** 2018/9/25 17:05:08 : 程序启动 2018/9/25 17:05:08 : 程序结束 :放在callback中的EndInvoke不会阻塞主线程 2018/9/25 17:05:08 : Ticks 2018/9/25 17:05:08 : Begin Call TaskFunc(1) 2018/9/25 17:05:08 : Begin Call TaskFunc(2) 2018/9/25 17:05:09 : Ticks 2018/9/25 17:05:10 : Ticks 2018/9/25 17:05:10 : End Call TaskFunc(1) 2018/9/25 17:05:10 : End Call TaskFunc(2) :两个任务同时开始同时结束,总耗时2秒 ***********************************************************/ // 主线程、两个TaskFunc并行运行 // 可以在AsyncCallback种添加TaskFunc结束后的代码,并且不会阻塞主线程 private static void Test5() { Action a1 = () => { TaskFunc(1); }; Action a2 = () => { TaskFunc(2); }; a1.BeginInvoke(new AsyncCallback(iar => { a1.EndInvoke(iar); }), null); a2.BeginInvoke(new AsyncCallback(iar => { a2.EndInvoke(iar); }), null); } /*********************************************************** 2018/9/25 17:06:48 : 程序启动 2018/9/25 17:06:48 : 程序结束 :await没有阻塞主线程 2018/9/25 17:06:48 : Ticks 2018/9/25 17:06:48 : Begin Call TaskFunc(2) 2018/9/25 17:06:48 : Begin Call TaskFunc(1) 2018/9/25 17:06:49 : Ticks 2018/9/25 17:06:50 : Ticks 2018/9/25 17:06:50 : End Call TaskFunc(1) 2018/9/25 17:06:50 : End Call TaskFunc(2) 2018/9/25 17:06:50 : End Call Test6() :阻塞异步调用,等待任务完成 ***********************************************************/ // 主线程、两个TaskFunc并行运行 // await没有阻塞主线程,达到了Test5相似的功能,代码更简洁 private static async void Test6() { Task t1 = Task.Run(() => { TaskFunc(1); }); Task t2 = Task.Run(() => { TaskFunc(2); }); await Task.WhenAll(t1, t2); Console.WriteLine(DateTime.Now.ToString() + " : " + "End Call Test6()"); } /*********************************************************** 2018/9/25 17:10:20 : 程序启动 2018/9/25 17:10:21 : Begin Call TaskFunc(1) 2018/9/25 17:10:22 : Begin Call TaskFunc(2) 2018/9/25 17:10:23 : 程序结束 2018/9/25 17:10:23 : Ticks 2018/9/25 17:10:23 : End Call TaskFunc(1) 2018/9/25 17:10:24 : End Call TaskFunc(2) 2018/9/25 17:10:24 : End Call Test6() 2018/9/25 17:10:24 : Ticks 2018/9/25 17:10:25 : Ticks 2018/9/25 17:10:26 : Ticks 2018/9/25 17:10:27 : End Call Test6() ***********************************************************/ // await之前的Sleep阻塞了主线程,await之后的Sleep没有阻塞主线程 private static async void Test7() { Thread.Sleep(1000); Task t1 = Task.Run(() => { TaskFunc(1); }); Thread.Sleep(1000); Task t2 = Task.Run(() => { TaskFunc(2); }); Thread.Sleep(1000); await Task.WhenAll(t1, t2); Console.WriteLine(DateTime.Now.ToString() + " : " + "End Call Test6()"); Thread.Sleep(3000); Console.WriteLine(DateTime.Now.ToString() + " : " + "End Call Test6()"); } /*********************************************************** 2018/9/25 17:14:01 : 程序启动 2018/9/25 17:14:04 : Begin Call TaskFunc(1) 2018/9/25 17:14:06 : End Call TaskFunc(1) 2018/9/25 17:14:07 : Begin Call TaskFunc(2) 2018/9/25 17:14:09 : End Call TaskFunc(2) 2018/9/25 17:14:10 : End Call Test6() 2018/9/25 17:14:13 : End Call Test6() 2018/9/25 17:14:13 : 程序结束 ***********************************************************/ // 在await调用的时候,两个Task都已经结束了,await之后的Sleep也阻塞了主线程 private static async void Test8() { Thread.Sleep(3000); Task t1 = Task.Run(() => { TaskFunc(1); }); Thread.Sleep(3000); Task t2 = Task.Run(() => { TaskFunc(2); }); Thread.Sleep(3000); await Task.WhenAll(t1, t2); Console.WriteLine(DateTime.Now.ToString() + " : " + "End Call Test6()"); Thread.Sleep(3000); Console.WriteLine(DateTime.Now.ToString() + " : " + "End Call Test6()"); } }