首先不是阻塞式的,async await 是通过csp的方式实现的无堆栈携程,await 在编译的时候会把await后的代码转换成状态机的下一步【可以简单理解为await之前的代码是Task里面执行,之后的代码放到了Task.ContinueWith里面执行】,但是线程并没阻塞, 在等待IO返回的时候线程会去调度执行下一个Task的方法,并未原地阻塞。这样就提高了线程的利用率。 相比Golang和java的有栈协程,无栈携程性能更好,资源占用更少,但是缺点是代码侵入比较强,因为async await具有代码传染性。所以C#的Task单个进程可以创建百万个然后同时调度,但是Golang的gorutine却不行。
目录
回到顶部
结论
同步还是异步,区别如下:
- 同步:你使用 await 修饰符去调用一个异步(async)方法(是异步方法,不过是阻塞式的,可简单理解为同步);
- 异步:你获取异步方法返回的 Task,就是异步(后文有代码示例);
- 可以实现多任务执行顺序执行且不阻塞。
概述
async(C# 参考)
async 是“异步”的简写,使用 async 修饰符可将方法、lambda 表达式或匿名方法指定为异步。
如果对方法或表达式使用此修饰符,则其称为异步方法 。
如下示例定义了一个名为 ExampleMethodAsync
的异步方法:
public async Task<int> ExampleMethodAsync() { //... }
await(C# 参考)
await 运算符暂停对其所属的 async 方法的求值,直到其操作数表示的异步操作完成。
异步操作完成后,await 运算符将返回操作的结果(如果有)。
当 await 运算符应用到表示已完成操作的操作数时,它将立即返回操作的结果,而不会暂停其所属的方法。
await 运算符不会阻止计算异步方法的线程。
当 await 运算符暂停其所属的异步方法时,控件将返回到方法的调用方。
官网说的话是不是听不明白?没关系,你就理解为添加了await 修饰符后,必须要等调用返回后才能继续执行下一步。
如下示例:
public async Task<int> ExampleMethodAsync() { //... //这添加了await,执行到这里需要等待MethodAwiat方法执行完成后才会往下继续执行。 int result = await MethodAwiat(); //... } public async Task<int> MethodAwiat() { //... }回到顶部
作用及基本用法
await和async可以简化我们异步编程,也可以让我们以一种类似同步编程的方式来进行异步编程。
另外当我们需要不阻塞主线程异步执行,但又要有顺序的执行相关代码的时候,await/async就可以排上用场。
基本用法如下示例:
/// <summary> /// 可异步可同步 /// </summary> /// <returns></returns> public async Task<string> TestAsync() { Thread.Sleep(3000); Task<string> task = new Task<string>(() => { return "主程序返回后我还在执行数据:我是复杂的异步执行的方法,不阻碍主程序执行。。。。。"; }); task.Start(); return await task; } // await 使得任务同步 public async void T1() { // 使用 await 关键字,代表等待执行完成,同步 string time = await TestAsync(); Console.WriteLine(time); Console.WriteLine("执行完毕"); } // 直接获得返回的 Task,实现异步 public void T2() { // 获取 Task 任务对象,后面的逻辑过程可以弄成异步 Task<string> task = TestAsync(); // 这里可以处理其它事情,处理完毕后,再获取执行结果 Console.WriteLine("执行完毕"); Console.WriteLine(task.Result); }
回到顶部
实现多任务顺序执行且不阻塞
以微软文档的做早餐的案例加以简化来讲解 【使用Async和Await可以实现多任务顺序执行且不阻塞】。
主线任务任务:倒橙汁 -> 烤面包 -> 煎培根 -> 煎鸡蛋 -> 倒咖啡。
1.同步执行
我们任务逐步顺序执行如下,耗时:17064毫秒(17.064秒):
using Microsoft.International.Converters.TraditionalChineseToSimplifiedConverter; using System; using System.Diagnostics; using System.Threading; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); PourOJ(); PourCoffee(); ToastBread(); FryBacon(); FryEggs(); Console.WriteLine("早餐已经做完!"); stopwatch.Stop(); Console.WriteLine($"做早餐总计耗时:{stopwatch.ElapsedMilliseconds}"); Console.ReadLine(); } //倒橙汁 private static void PourOJ() { Thread.Sleep(1000); Console.WriteLine("倒一杯橙汁"); } //烤面包 private static void ToastBread() { Console.WriteLine("开始烤面包"); Thread.Sleep(3000); Console.WriteLine("烤面包好了"); } //煎培根 private static void FryBacon() { Console.WriteLine("开始煎培根"); Thread.Sleep(6000); Console.WriteLine("培根煎好了"); } //煎鸡蛋 private static void FryEggs() { Console.WriteLine("开始煎鸡蛋"); Thread.Sleep(6000); Console.WriteLine("鸡蛋好了"); } //倒咖啡 private static void PourCoffee() { Thread.Sleep(1000); Console.WriteLine("倒咖啡"); } } }
2.并行执行
如果此时我们每一项任务都有一个单独的人去完成,将会缩短时间将近3倍(当然不能单纯的用时间量化他),
如下,耗时6041毫秒(6.041秒):
using Microsoft.International.Converters.TraditionalChineseToSimplifiedConverter; using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Test(); Console.ReadLine(); } private static void Test() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); List<Task> tasks = new List<Task>() { PourOJ(), ToastBread(), FryBacon(), FryEggs(), PourCoffee() }; Task.WhenAll(tasks).ContinueWith((t) => { Console.WriteLine("早餐已经做完!"); stopwatch.Stop(); Console.WriteLine($"做早餐总计耗时:{stopwatch.ElapsedMilliseconds}"); }); } //倒橙汁 private static async Task PourOJ() { await Task.Delay(1000); Console.WriteLine("倒一杯橙汁"); } //烤面包 private static async Task ToastBread() { Console.WriteLine("开始烤面包"); await Task.Delay(3000); Console.WriteLine("烤面包好了"); } //煎培根 private static async Task FryBacon() { Console.WriteLine("开始煎培根"); await Task.Delay(6000); Console.WriteLine("培根煎好了"); } //煎鸡蛋 private static async Task FryEggs() { Console.WriteLine("开始煎鸡蛋"); await Task.Delay(6000); Console.WriteLine("鸡蛋好了"); } //倒咖啡 private static async Task PourCoffee() { await Task.Delay(1000); Console.WriteLine("倒咖啡"); } } }
3.并行且可指定顺序执行
但是我们不可能每次做早餐都有那么多人同时做,
需求变更:如果现在要求,先倒橙汁,然后倒咖啡,其余的操作并行执行,应该如何操作呢?
只需将以上案例的主线程Test 方法修改如下,其他的不变,耗时8073毫秒(8.073秒):
private static async void Test() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); await PourOJ(); //倒橙汁 await PourCoffee(); //然后倒咖啡 List<Task> tasks = new List<Task>() { ToastBread(), FryBacon(), FryEggs() };//子任务List await Task.WhenAll(tasks); //其余的并行操作 Console.WriteLine("早餐已经做完!"); stopwatch.Stop(); Console.WriteLine($"做早餐总计耗时:{stopwatch.ElapsedMilliseconds}"); }回到顶部
总结
我们发现,用好了async和await确实很节省时间。
大家可以想想怎么优雅的在异步里面开多线程(多主线程多子线程)最节省时间。
标签:异步,Task,Console,await,这篇,WriteLine,async From: https://www.cnblogs.com/chinasoft/p/16995483.html