以前写异步操作的时候要不就是“直接定义一个Thread线程或者往线程池中扔一个委托方法”、“要不就是定一个委托变量然后通过委托的BeginInvoke和EndInvoke来实现异步操作以及异步操作结果的收集”。虽然.可跨平台的Net Core 3.0都出来了,.Net framework也干到4.7了,但是多年来写代码一直使用那两种比较传统的方式,今天闲来无事研究研究.Net 4上新加进来的任务Task,发现这东西确实不错,虽然本质上也是开一个线程出去,不过跟传统写法比起来好处确实大大的,主要总结如下几点:
使用Task的好处:
1、Task本身其实也是一个线程;
2、Task可以控制线程的先后执行顺序,通过Task.WaitAll(t1, t4);可以等待指定任务执行完再继续执行其它代码;
3、通过使用CancellationTokenSource的Cancel()结合Task方法内部的CancellationTokenSource.Token.ThrowIfCancellationRequested()可以取消某个正在运行的任务,
如果多个任务方法中都有CancellationTokenSource.Toke.ThrowIfCancellationRequested()方法,那么还可以用一个开关一下取消多个任务;
4、执行完的Task可以查看其执行状态,是取消、正常完成、还是异常出错(同样是动作执行完成,如果用传统代码自己往出死磕估计费很多代码量);
5、以上几点虽然通过传统的代码逻辑也能实现,但是没有使用Task直观,需要自己实现很多逻辑控制代码;
*使用async结合await实现异步方法的好处:
1、比较直观,可以将传统需要写在BeginInvoke和EndInvoke的代码写在同一个方法中,中间用await隔开就行,await放到比较耗时的方法前边;
2、async和await必须同时出现,await必须出现在有async修饰的方法中,其实await语句的下一句就是endinvoke中需要执行的代码;
3、调用async方法时,如果主调代码是在带有async修饰的方法中,那么在调用异步方法M2时前边加上await,直接用返回类型接住结果即可;
*4、如果调用async方法所在的代码方法外围没有async修饰符的方法中时,那么只能直接调用M2,而且需要用Task<返回类型>来接住结果,列如Task<int> w,这时候是异步调用, 如果想同步取结果,那么直接调用w.Result就可以达到阻塞的目的,当然阻塞之前是可以先执行一些其它代码的;
废话少说,直接上代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace TestTaskAndAsync { class Program { static void Main(string[] args) { //是否执行Task测试代码 bool isTestTask = false; if (isTestTask) //Task测试代码 { /* * 使用Task的好处: * 1、Task本身其实也是一个线程; * 2、Task可以控制线程的先后执行顺序,通过Task.WaitAll(t1, t4);可以等待指定任务执行完再继续执行其它代码; * 3、通过使用CancellationTokenSource的Cancel()结合Task方法内部的CancellationTokenSource.Toke.ThrowIfCancellationRequested()可以取消某个正在运行的任务, * 如果多个任务方法中都有CancellationTokenSource.Token.ThrowIfCancellationRequested()方法,那么还可以一下取消多个任务; * 4、执行完的Task可以查看其执行状态,是取消、正常完成、还是异常出错; * 5、以上几点虽然通过传统的代码逻辑也能实现,但是没有使用Task直观,需要自己实现很多逻辑控制代码; * * **/ try { #region 定义取消标记,使用这种标记可以结合 token.ThrowIfCancellationRequested(); 同时取消多个执行的Task CancellationTokenSource tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; #endregion #region Task t1 Task t1 = new Task(() => { Console.WriteLine("Task t1 开始执行!"); System.Threading.Thread.Sleep(2000); }); t1.ContinueWith(task => { Console.WriteLine(Environment.NewLine + "Task t1执行完成,状态为: IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); Console.WriteLine(); tokenSource.Cancel(); //触发取消操作 Console.WriteLine("成功在t1任务中发送取消信号!"); }); t1.Start(); #endregion #region Task t2 Task t2 = new Task(() => { try { Console.WriteLine("Task t2 开始执行!"); for (int i = 0; i < 20; i++) { token.ThrowIfCancellationRequested(); System.Threading.Thread.Sleep(200); Console.WriteLine($"t2打印:{i}"); } } catch (Exception ex) { throw ex; } }, token); t2.ContinueWith(task => { Console.WriteLine(Environment.NewLine + "Task t2执行完成,状态为: IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); Console.WriteLine(); }); t2.Start(); #endregion #region Task t3 Task t3 = new Task(() => { Console.WriteLine("Task t3 开始执行!"); int a = 0; int b = 100 / a; //如果不是Cancellation抛出的异常,那么IsFalut就是True }, token); t3.ContinueWith(task => { Console.WriteLine(Environment.NewLine + "Task t3执行完成,状态为: IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); }); t3.Start(); #endregion #region Task t4 Task t4 = new Task(() => { Console.WriteLine("Task t4 开始执行!"); for (int i = 0; i < 20; i++) { //token.ThrowIfCancellationRequested(); System.Threading.Thread.Sleep(200); Console.WriteLine($"t4打印:{i}"); } }, token); t4.ContinueWith(task => { Console.WriteLine(Environment.NewLine + "Task t4执行完成,状态为: IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); Console.WriteLine(); }); t4.Start(); #endregion Task.WaitAll(t1, t4); Console.WriteLine(Environment.NewLine + "等待t1、t4完成后, 对Task的主调完成!"); Console.WriteLine(); } catch (Exception ex) { //这里可以捕获到Task内部抛出的异常 Console.WriteLine($"异常:{ex.InnerException.Message}"); } Console.ReadLine(); } else { /* * 使用async结合await实现异步方法的好处: * 1、比较直观,可以将传统需要写在BeginInvoke和EndInvoke的代码写在同一个方法中,中间用await隔开就行,await放到比较耗时的方法前边; * 2、async和await必须同时出现,await必须出现在有async修饰的方法中,其实await语句的下一句就是endinvoke中需要执行的代码; * 3、调用async方法时,如果主调代码是在带有async修饰的方法中,那么在调用M2时前边加上await,直接用返回类型接住结果即可; * 4、如果调用async方法所在的代码方法外围没有async修饰符,那么只能直接调用需要用Task<返回类型>来接住结果,列如Task<int> w,这时候是异步调用,如果 * 想同步取结果,那么直接调用w.Result就可以达到阻塞的目的,当然阻塞之前是可以先执行一些其它代码的; * **/ #region string reqm1 = Guid.NewGuid().ToString(); string reqm2 = Guid.NewGuid().ToString(); M1(reqm1); Task<int> w = M2(5, 7, reqm2); Console.WriteLine("main 主调完成!"); Console.WriteLine($"main中直调M2打印结果:{w.Result}"); Console.ReadLine(); #endregion } } static async void M1(string request) { Console.WriteLine($"{request}:M1开始调用M2!"); int k = await M2(3,5,request); Console.WriteLine($"{request}:M1调用M2完成,返回结果为:{k}"); } /// <summary> /// /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> static async Task<int> M2(int a, int b,string request) { Console.WriteLine($"{request}:M2 开始执行,传入参数为:{a},{b}"); await Task.Delay(2000); Console.WriteLine($"{request}:M2 开始计算结果,传入参数为:{a},{b}"); return a + b; } } }
我的学习成果分享一下,欢迎大家指正!
原文:https://www.cnblogs.com/Taburensheng/p/12050369.html