第十节:利用async和await简化异步编程模式的几种写法
一. async和await简介
PS:简介
1. async和await这两个关键字是为了简化异步编程模型而诞生的,使的异步编程跟简洁,它本身并不创建新线程,但在该方法内部开启多线程,则另算。
2. 这两个关键字适用于处理一些文件IO操作。
3. 好处:代码简介,把异步的代码写成了同步的形式,提高了开发效率。
坏处:如果使用同步思维去理解,容易出问题,返回值对不上。
二. 几种用法
情况1:当只有async,没有await时,方法会有个警告,和普通的多线程方法没有什么区别,不存在线程等待的问题。
代码实践:
1 private static async void Test1() 2 { 3 //主线程执行 4 Console.WriteLine("主线程{0}开始:", Thread.CurrentThread.ManagedThreadId); 5 //启动新线程完成任务 6 Task task = Task.Run(() => 7 { 8 Console.WriteLine("子线程{0}开始:", Thread.CurrentThread.ManagedThreadId); 9 Thread.Sleep(3000); 10 Console.WriteLine("子线程{0}结束:", Thread.CurrentThread.ManagedThreadId); 11 }); 12 //主线程执行 13 Console.WriteLine("主线程{0}结束:", Thread.CurrentThread.ManagedThreadId); 14 }
代码结果:
情况2:不推荐void返回值,使用Task来代替Task和Task<T>能够使用await, Task.WhenAny, Task.WhenAll等方式组合使用,async Void 不行。
代码实践:
1 /// <summary> 2 /// 不推荐void返回值,使用Task来代替 3 /// Task和Task<T>能够使用await, Task.WhenAny, Task.WhenAll等方式组合使用。async Void 不行 4 /// </summary> 5 private static async void Test2() 6 { 7 //主线程执行 8 Console.WriteLine("主线程{0}开始:", Thread.CurrentThread.ManagedThreadId); 9 //启动新线程完成任务 10 Task task = Task.Run(() => 11 { 12 Console.WriteLine("子线程{0}开始:", Thread.CurrentThread.ManagedThreadId); 13 Thread.Sleep(3000); 14 Console.WriteLine("子线程{0}结束:", Thread.CurrentThread.ManagedThreadId); 15 }); 16 await task; //等待子线程执行完毕,方可执行后面的语句 17 Console.WriteLine("主线程{0}结束:", Thread.CurrentThread.ManagedThreadId); 18 }
代码结果:
情况3:async Task == async void。 区别:Task和Task<T>能够使用await, Task.WhenAny, Task.WhenAll等方式组合使用,async Void 不行。
代码实践:
1 /// <summary> 2 /// 无返回值 async Task == async void 3 /// Task和Task<T>能够使用await, Task.WhenAny, Task.WhenAll等方式组合使用,async Void 不行 4 /// </summary> 5 private static async Task Test3() 6 { 7 //主线程执行 8 Console.WriteLine("主线程{0}开始:", Thread.CurrentThread.ManagedThreadId); 9 //启动新线程完成任务 10 Task task = Task.Run(() => 11 { 12 Console.WriteLine("子线程{0}开始:", Thread.CurrentThread.ManagedThreadId); 13 Thread.Sleep(3000); 14 Console.WriteLine("子线程{0}结束:", Thread.CurrentThread.ManagedThreadId); 15 }); 16 await task; //等待子线程执行完毕,方可执行后面的语句 17 Console.WriteLine("主线程{0}结束:", Thread.CurrentThread.ManagedThreadId); 18 }
代码结果:
情况4和情况5:说明要使用子线程中的变量,一定要等子线程执行结束后再使用。
代码实践:
1 /// <summary> 2 /// 带返回值的Task,要使用返回值,一定要等子线程计算完毕才行 3 /// </summary> 4 /// <returns></returns> 5 private static async Task<long> Test4() 6 { 7 //主线程执行 8 Console.WriteLine("主线程{0}开始:", Thread.CurrentThread.ManagedThreadId); 9 long result = 0; 10 //启动新线程完成任务 11 Task task = Task.Run(() => 12 { 13 for (long i = 0; i < 100; i++) 14 { 15 result += i; 16 } 17 }); 18 await task; //等待子线程执行完毕,方可执行后面的语句 19 Console.WriteLine("主线程{0}结束:", Thread.CurrentThread.ManagedThreadId); 20 Console.WriteLine("result:{0}", result); 21 return result; 22 }
1 /// <summary> 2 /// 带返回值的Task,要使用返回值,一定要等子线程计算完毕才行 3 /// 与情况四形成对比,没有等待,最终结果不准确 4 /// </summary> 5 /// <returns></returns> 6 private static Task<long> Test5() 7 { 8 //主线程执行 9 Console.WriteLine("主线程{0}开始:", Thread.CurrentThread.ManagedThreadId); 10 long result = 0; 11 //启动新线程完成任务 12 TaskFactory taskFactory = new TaskFactory(); 13 Task<long> task = taskFactory.StartNew<long>(() => 14 { 15 for (long i = 0; i < 100; i++) 16 { 17 result += i; 18 } 19 return 1; 20 }); 21 Console.WriteLine("主线程{0}结束:", Thread.CurrentThread.ManagedThreadId); 22 Console.WriteLine("result:{0}", result); 23 return task; 24 }
代码结果:
以上两种情况,第一种情况含有线程等待的结果为4950,第二个情况么有线程等待,结果不准确(即共享变量竞用问题)。