async和await的使用总结 ~ 竟然一直用错了c#中的async和await的使用。。
对于c#中的async和await的使用,没想到我一直竟然都有一个错误。。
。。还是总结太少,这里记录下。
这里以做早餐为例
流程如下:
- 倒一杯咖啡。
- 加热平底锅,然后煎两个鸡蛋。
- 煎三片培根。
- 烤两片面包。
- 在烤面包上加黄油和果酱。
- 倒一杯橙汁。
当使用同步方式实现时,代码是这样的:
using System;
using System.Diagnostics;
using System.Threading.Tasks; namespace AsyncBreakfast
{
class Program
{
static void Main(string[] args)
{
var sw = new Stopwatch();
sw.Start();
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready"); Egg eggs = FryEggs();
Console.WriteLine("eggs are ready"); Bacon bacon = FryBacon();
Console.WriteLine("bacon is ready"); Toast toast = ToastBread();
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready"); Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!"); Console.WriteLine($"totol time:{sw.ElapsedMilliseconds/1000}");
Console.ReadKey();
} private static Juice PourOJ()
{
Console.WriteLine("Pouring orange juice");
return new Juice();
} private static void ApplyJam(Toast toast) =>
Console.WriteLine("Putting jam on the toast"); private static void ApplyButter(Toast toast) =>
Console.WriteLine("Putting butter on the toast"); private static Toast ToastBread(int slices)
{
for (int slice = ; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
Task.Delay().Wait();
Console.WriteLine("Remove toast from toaster"); return new Toast();
} private static Bacon FryBacon(int slices)
{
Console.WriteLine($"putting {slices} slices of bacon in the pan");
Console.WriteLine("cooking first side of bacon...");
Task.Delay().Wait();
for (int slice = ; slice < slices; slice++)
{
Console.WriteLine("flipping a slice of bacon");
}
Console.WriteLine("cooking the second side of bacon...");
Task.Delay().Wait();
Console.WriteLine("Put bacon on plate"); return new Bacon();
} private static Egg FryEggs(int howMany)
{
Console.WriteLine("Warming the egg pan...");
Task.Delay().Wait();
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
Task.Delay().Wait();
Console.WriteLine("Put eggs on plate"); return new Egg();
} private static Coffee PourCoffee()
{
Console.WriteLine("Pouring coffee");
return new Coffee();
}
}
class Coffee { }
class Egg { }
class Bacon { }
class Toast { }
class Juice { }
}
运行效果如下:

或表示为这样

同步准备的早餐大约花费了 30 分钟,因为总耗时是每个任务耗时的总和。这里的total time只是用来表示记录下程序运行的时间。
而我以前写的异步代码是这样的:
using System;
using System.Diagnostics;
using System.Threading.Tasks; namespace AsyncBreakfast
{
class Program
{
static async void Main(string[] args)
{
var sw = new Stopwatch();
sw.Start();
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready"); Egg eggs = await FryEggsAsync();
Console.WriteLine("eggs are ready"); Bacon bacon = await FryBaconAsync();
Console.WriteLine("bacon is ready"); Toast toast = await ToastBreadAsync();
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready"); Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!"); Console.WriteLine($"totol time:{sw.ElapsedMilliseconds/1000}");
Console.ReadKey();
} static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
var toast = await ToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast); return toast;
} private static Juice PourOJ()
{
Console.WriteLine("Pouring orange juice");
return new Juice();
} private static void ApplyJam(Toast toast) =>
Console.WriteLine("Putting jam on the toast"); private static void ApplyButter(Toast toast) =>
Console.WriteLine("Putting butter on the toast"); private static async Task<Toast> ToastBreadAsync(int slices)
{
for (int slice = ; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
await Task.Delay();
Console.WriteLine("Remove toast from toaster"); return new Toast();
} private static async Task<Bacon> FryBaconAsync(int slices)
{
Console.WriteLine($"putting {slices} slices of bacon in the pan");
Console.WriteLine("cooking first side of bacon...");
await Task.Delay();
for (int slice = ; slice < slices; slice++)
{
Console.WriteLine("flipping a slice of bacon");
}
Console.WriteLine("cooking the second side of bacon...");
await Task.Delay();
Console.WriteLine("Put bacon on plate"); return new Bacon();
} private static async Task<Egg> FryEggsAsync(int howMany)
{
Console.WriteLine("Warming the egg pan...");
await Task.Delay();
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
await Task.Delay();
Console.WriteLine("Put eggs on plate"); return new Egg();
} private static Coffee PourCoffee()
{
Console.WriteLine("Pouring coffee");
return new Coffee();
} }
class Coffee { }
class Egg { }
class Bacon { }
class Toast { }
class Juice { }
}
效果如下:

可以看出,这样编写的异步和最初同步版本的总共的耗时大致相同。
这是因为这段代码还没有利用异步编程的某些关键功能。
即上面的异步代码的使用在这里是不准确的。
可以看出,这段代码里面的打印输出与同步是一样的。
这是因为:在煎鸡蛋或培根时,此代码虽然不会阻塞,但是此代码也不会启动任何其他任务。
就造成了异步煎鸡蛋的操作完成后,才会开始培根制作。
但是,对于这里而言,我不希望每个任务都按顺序依次执行。
最好是首先启动每个组件任务,然后再等待之前任务的完成。
例如:首先启动鸡蛋和培根。
同时启动任务
在很多方案中,你可能都希望立即启动若干独立的任务。然后,在每个任务完成时,你可以继续
进行已经准备的其他工作。
就像这里同时启动煎鸡蛋,培根和烤面包。
我们这里对早餐代码做些更改。
正确的做法
第一步是存储任务以便在这些任务启动时进行操作,而不是等待:
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready"); Task<Egg> eggsTask = FryEggsAsync();
Egg eggs = await eggsTask;
Console.WriteLine("eggs are ready"); Task<Bacon> baconTask = FryBaconAsync();
Bacon bacon = await baconTask;
Console.WriteLine("bacon is ready"); Task<Toast> toastTask = ToastBreadAsync();
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready"); Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
接下来,可以在提供早餐之前将用于处理培根和鸡蛋的await语句移动到此方法的末尾:
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready"); Task<Egg> eggsTask = FryEggsAsync();
Task<Bacon> baconTask = FryBaconAsync();
Task<Toast> toastTask = ToastBreadAsync(); Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready"); Egg eggs = await eggsTask;
Console.WriteLine("eggs are ready");
Bacon bacon = await baconTask;
Console.WriteLine("bacon is ready"); Console.WriteLine("Breakfast is ready!");
运行效果如下:

或者

可以看出,这里一次启动了所有的异步任务。而你仅在需要结果时,才会等待每项任务。
这里异步准备的造成大约花费20分钟,这是因为一些任务可以并发进行。
而对于直接 Egg eggs = await FryEggsAsync(2); 的方式,适用于你只需要等待这一个异步操作结果,不需要进行其他操作的时候。
与任务组合
吐司操作由异步操作(烤面包)和同步操作(添加黄油和果酱)组成。
这里涉及到一个重要概念:
异步操作后跟同步操作的这种组合也是一个异步操作。
也就是说,如果操作的任何部分是异步的,整个操作就是异步的。
代码如下:
static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
var toast = await ToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast); return toast;
}
所有,主要代码块现在变为:
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready"); var eggsTask = FryEggsAsync();
var baconTask = FryBaconAsync();
var toastTask = MakeToastWithButterAndJamAsync(); var eggs = await eggsTask;
Console.WriteLine("eggs are ready"); var bacon = await baconTask;
Console.WriteLine("bacon is ready"); var toast = await toastTask;
Console.WriteLine("toast is ready"); Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
高效的等待任务
可以通过使用Task类的方法改进上述代码末尾一系列await语句。
WhenAll 是其中的一个api , 它将返回一个其参数列表中的所有任务都已完成时猜完成的Task,
代码如下
await Task.WhenAll(eggsTask, baconTask, toastTask);
Console.WriteLine("eggs are ready");
Console.WriteLine("bacon is ready");
Console.WriteLine("toast is ready");
Console.WriteLine("Breakfast is ready!");
另一种选择是 WhenAny, 它将返回一个,当其参数完成时猜完成的 Task<Task>。
var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > )
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Console.WriteLine("eggs are ready");
}
else if (finishedTask == baconTask)
{
Console.WriteLine("bacon is ready");
}
else if (finishedTask == toastTask)
{
Console.WriteLine("toast is ready");
}
breakfastTasks.Remove(finishedTask);
}
处理已完成任务的结果之后,可以从传递给 WhenAny 的任务列表中删除此已完成的任务。
进行这些更改后,代码的最终版本将如下所示:
using System;
using System.Collections.Generic;
using System.Threading.Tasks; namespace AsyncBreakfast
{
class Program
{
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready"); var eggsTask = FryEggsAsync();
var baconTask = FryBaconAsync();
var toastTask = MakeToastWithButterAndJamAsync(); var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > )
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Console.WriteLine("eggs are ready");
}
else if (finishedTask == baconTask)
{
Console.WriteLine("bacon is ready");
}
else if (finishedTask == toastTask)
{
Console.WriteLine("toast is ready");
}
breakfastTasks.Remove(finishedTask);
} Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
} static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
var toast = await ToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast); return toast;
} private static Juice PourOJ()
{
Console.WriteLine("Pouring orange juice");
return new Juice();
} private static void ApplyJam(Toast toast) =>
Console.WriteLine("Putting jam on the toast"); private static void ApplyButter(Toast toast) =>
Console.WriteLine("Putting butter on the toast"); private static async Task<Toast> ToastBreadAsync(int slices)
{
for (int slice = ; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
await Task.Delay();
Console.WriteLine("Remove toast from toaster"); return new Toast();
} private static async Task<Bacon> FryBaconAsync(int slices)
{
Console.WriteLine($"putting {slices} slices of bacon in the pan");
Console.WriteLine("cooking first side of bacon...");
await Task.Delay();
for (int slice = ; slice < slices; slice++)
{
Console.WriteLine("flipping a slice of bacon");
}
Console.WriteLine("cooking the second side of bacon...");
await Task.Delay();
Console.WriteLine("Put bacon on plate"); return new Bacon();
} private static async Task<Egg> FryEggsAsync(int howMany)
{
Console.WriteLine("Warming the egg pan...");
await Task.Delay();
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
await Task.Delay();
Console.WriteLine("Put eggs on plate"); return new Egg();
} private static Coffee PourCoffee()
{
Console.WriteLine("Pouring coffee");
return new Coffee();
}
}
}
效果如下:

或者

这种异步的代码实现最终大约花费15分钟,因为一些任务能同时运行,
并且该代码能够同时监视多个任务,只在需要时才执行操作。
总结:
async 和 await的功能最好能做到:
尽可能启动任务,不要在等待任务完成时造成阻塞。
即可以先把任务存储到task,然后在后面需要用的时候,调用await task()方法。
参考网址:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/
async和await的使用总结 ~ 竟然一直用错了c#中的async和await的使用。。的更多相关文章
- 理解C#中的 async await
前言 一个老掉牙的话题,园子里的相关优秀文章已经有很多了,我写这篇文章完全是想以自己的思维方式来谈一谈自己的理解.(PS:文中涉及到了大量反编译源码,需要静下心来细细品味) 从简单开始 为了更容易理解 ...
- [译] C# 5.0 中的 Async 和 Await (整理中...)
C# 5.0 中的 Async 和 Await [博主]反骨仔 [本文]http://www.cnblogs.com/liqingwen/p/6069062.html 伴随着 .NET 4.5 和 V ...
- ASP.NET 中的 Async/Await 简介
本文转载自MSDN 作者:Stephen Cleary 原文地址:https://msdn.microsoft.com/en-us/magazine/dn802603.aspx 大多数有关 async ...
- [C#] .NET4.0中使用4.5中的 async/await 功能实现异
好东西需要分享 原文出自:http://www.itnose.net/detail/6091186.html 在.NET Framework 4.5中添加了新的异步操作库,但是在.NET Framew ...
- 【TypeScript】如何在TypeScript中使用async/await,让你的代码更像C#。
[TypeScript]如何在TypeScript中使用async/await,让你的代码更像C#. async/await 提到这个东西,大家应该都很熟悉.最出名的可能就是C#中的,但也有其它语言也 ...
- 在MVC中使用async和await的说明
首先,在mvc中如果要用纯异步请不要使用async和await,可以直接使用Task.Run. 其次,在mvc中使用async和await可以让系统开新线程处理Task的代码,同时不必等Task执行结 ...
- JavaScript ES7 中使用 async/await 解决回调函数嵌套问题
原文链接:http://aisk.me/using-async-await-to-avoid-callback-hell/ JavaScript 中最蛋疼的事情莫过于回调函数嵌套问题.以往在浏览器中, ...
- 在Silverlight中使用async/await
现在 async/await 大行其道,确实,有了 async/await ,异步编程真是简单多了,个人觉得 async/await 的出现,给开发者还来的方便,绝不亚于当年 linq 的出现. 但要 ...
- 在现有代码中通过async/await实现并行
在现有代码中通过async/await实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...
随机推荐
- 洛谷 P4408 [NOI2003]逃学的小孩
题目传送门 题目描述 Chris家的电话铃响起了,里面传出了Chris的老师焦急的声音:“喂,是Chris的家长吗?你们的孩子又没来上课,不想参加考试了吗?”一听说要考试,Chris的父母就心急如焚, ...
- 微服务框架Demo.MicroServer运行手册
一.背景说明: 之前分享过一个微服务开发框架, "享一个集成.NET Core+Swagger+Consul+Polly+Ocelot+IdentityServer4+Exceptionle ...
- 小师妹学JVM之:JVM中的Safepoints
目录 简介 GC的垃圾回收器 分代回收器中的问题 safepoints safepoint一般用在什么地方 总结 简介 java程序员都听说过GC,大家也都知道GC的目的是扫描堆空间,然后将那些标记为 ...
- smtp 发送邮件实例
发送邮件的关键点在于邮箱服务器地址是否一致 //smtp 服务器地址,咨询 smtp 提供商,例如 smtp.126.net 这种格式,端口和服务器地址是配套的,一般是 465 或者 25 SmtpC ...
- day46 mysql进阶
目录 一.约束条件 1 default默认值 2 unique唯一 2.1 单列唯一 2.2 联合唯一 3 primary key主键 3.1 主键的基本使用 3.2 主键的特性 4 auto_inc ...
- python实现二维码、条形码识别
环境: python 3.7 Win7 依赖包安装: pip install pillow pip install opencv-python pip install opencv-contrib-p ...
- sql 增加修改表格属性字段
1.修改字段默认值 alter table 表名 drop constraint 约束名字 ------注解:删除表的字段的原有约束 alter table 表名 add constraint 约束名 ...
- Firefox 底部多出了一个白条
如图所示 下方多了个白条 html{ font-size:0px; }
- ScheduledThreadPoolExecutor源码主要部分解析
ScheduledThreadPoolExecutor继承与基础线程池类ThreadPoolExecutor并实现ScheduledExecutorService接口. 其中ScheduledExec ...
- 第三章:View的事件体系
3.1 View的基础知识 主要有:View的位置参数,MotionEvent和TouchSlop对象,VelocityTracker,GestureDetector和Scroller对象 3.1.1 ...