在处理比较耗时的操做(如图片处理、数据压缩、http请求等)传统的异步方法是直接使用Thread或者Task进行操做,在复杂的应用编写中可能会出现回调的问题,所以C#目前主要推荐使用async/await来进行异步操做。也就是async/await主要用来异步回调问题, 而真正的异步操做仍是用Task。编程
一般返回 Task 或 Task<TResult>。 在异步方法中,await 运算符应用于经过调用另外一个异步方法返回的任务安全
若是方法包含指定 TResult 类型操做数的 return 语句,将 Task<TResult> 指定为返回类型,若是方法不含任何 return 语句或包含不返回操做数的 return 语句,将 Task 用做返回类型多线程
异步方法也能够具备 void 返回类型。可是不推荐使用,由于没法等待具备 void 返回类型的异步方法,而且无效返回方法的调用方捕获不到异步方法引起的任何异常,并且也违背了咱们使用他的初衷--解决异步回调问题异步
还须要注意:异步方法既不能声明任何 in、ref 或 out 参数,也不能具备引用返回值,但它能够调用具备此类参数的方法async
假如如今要作饭,须要作米饭2秒,作汤2秒;同步的方法就是先作米饭等待2秒,而后作汤等待2秒;异步的方法,米饭和汤同时作,一共花2秒ide
代码以下异步编程
class Program { private static Stopwatch stopwatch = new Stopwatch(); static void Main(string[] args) { stopwatch.Start(); DoCook(); Console.ReadLine(); } /// <summary> /// Cooking /// </summary> /// <returns></returns> static async Task DoCook() { Console.WriteLine("Cook Start: " + stopwatch.ElapsedMilliseconds.ToString()); //case 1 异步Cooking var rice = DoRice(); var soup = DoSoup(); await rice; await soup; Console.WriteLine($"Cook End: {stopwatch.ElapsedMilliseconds.ToString() } - {(rice.Result + soup.Result).ToString()}"); } /// <summary> /// 作米饭,能够独立作 /// </summary> /// <returns></returns> static async Task<int> DoRice() { Console.WriteLine("DoRice Start: " + stopwatch.ElapsedMilliseconds.ToString()); var rice = 0; await Task.Run(() => { Thread.Sleep(2000); rice = 100; }); Console.WriteLine("DoRice End: " + stopwatch.ElapsedMilliseconds.ToString()); return rice; } /// <summary> /// 作汤,能够独立作 /// </summary> /// <returns></returns> static async Task<int> DoSoup() { Console.WriteLine("DoSoup Start: " + stopwatch.ElapsedMilliseconds.ToString()); var soup = 0; await Task.Run(() => { Thread.Sleep(2000); soup = 100; }); Console.WriteLine("DoSoup End: " + stopwatch.ElapsedMilliseconds.ToString()); return soup; } }
返回的结果函数
如今你女友很做,她非得吃蛋炒饭,蛋炒饭必须先作饭,假如作汤2秒,作蛋炒饭2秒钟,可是必须等米饭先作好性能
定义一个作蛋炒饭的方法ui
/// <summary> /// 作蛋炒饭,须要先作米饭 /// </summary> /// <returns></returns> static async Task<int> DoEggRice() { Console.WriteLine("DoEggRice Start: " + stopwatch.ElapsedMilliseconds.ToString()); var rice = await DoRice(); var eggRice = 0; await Task.Run(() => { Thread.Sleep(2000); eggRice = rice + 100; }); Console.WriteLine("DoEggRice End: " + stopwatch.ElapsedMilliseconds.ToString()); return eggRice; }
而后开始
/// <summary> /// Cooking /// </summary> /// <returns></returns> static async Task DoCook() { Console.WriteLine("Cook Start: " + stopwatch.ElapsedMilliseconds.ToString());//case 3 异步EggCooking var eggRice = DoEggRice(); var soup = DoSoup(); await eggRice; await soup; Console.WriteLine($"Cook End: {stopwatch.ElapsedMilliseconds.ToString() } - {(eggRice.Result + soup.Result).ToString()}"); }
结果以下
注意:
引用问答
异步必定能提升效率吗?
不必定。异步本质上仍是多线程,只是简化多线程的实现方式。至于使用多线程编程时可否提升程序执行效率,取决于 CPU 核心数,计算任务的复杂度以及该项任务自己是否适合被切分为并行计算模块。过于频繁地将不适合并行计算的任务拆分红异步编程中去,反而会致使密集计算性能的降低,由于此时线程池会疲于应对大量的线程调度操做。
有 async 必定要有 await 吗?不必定。在标记为 async 的方法中,没必要须出现 await 关键字,只是若没有 await 关键字,这个方法不是真正意义上的异步方法,它会与普通方法同样是同步执行的。编译器不会报错,但会给出提示。
相反,若要使用 await 关键字,则必须在方法签名中包含 async 关键字。不然 await 将被当作标识符,而不能被当作一个关键字来处理。也就是说,当一个方法的签名中不包含 async 关键字时,你甚至能够在方法体中把 await 做为变量名。但这种操做是极其不推荐的,很容易形成误导。
异步方法的名称必定要以「Async」为结尾吗?
不必定。这只是习惯问题,就跟微软推荐全部的自定义特性后面都以「Attributes」为结尾同样,这不是必须的,只是若是你们都这样作了,理解起来更加方便一些。具体状况取决于不一样场合下的规范要求。
使用 Task 而且 Run 了以后就实现异步了吗?
不是,这只是进行了一次多线程操做,后面的语句仍是同步执行的。直到碰见 await 关键字,随着控制权的返回,才真正能实现异步。
异步是线程安全的吗?
理论上是的,这也是为何异步编程模型可以极大地简化传统多线程操做所带来的各类问题的一大缘由。尽管 await 所指的对象运行在其余线程上,但其后的语句仍是会在原始线程上被执行。更深层次地说,后续的语句其实是使用 Task 的 ContinueWith 方法来实现的。因此咱们大能够放心的在异步方法中修改诸如 UI 元素等由主线程管理的资源。
可是,异步编程模型只是简化了这个过程,而不能替代咱们解决具体的数据同步问题。若是在 await 以后有对其余共享资源的访问,而在 await 获取执行结果以前,这些资源已经被其余线程修改,那么 await 后续语句执行时所面对的数据内容将是不可预测的。
异步必定是返回控制权与等待结果同时进行的吗?
第一时间返回控制权是必定的,而等待与否要看任务执行的状态。当程序遇到 await 关键字时,若是 Task 所指代的对象以极快的速度完成,那么异步方法内部就会以同步执行的方式继续向后执行 await 语句后面的操做,不会产生等待。只有当 Task 没有执行完毕时,才会进行等待
参考文献
https://blog.csdn.net/qc530167365/article/details/83108848
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/
https://www.jianshu.com/p/1136e79d96e6
https://www.jianshu.com/p/8ea7ed4a2493