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实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...
随机推荐
- 诊断Java线程死锁
比如我们有运行这样一个程序: 了解多线程的小伙版都知道,这段代码不会有打印结果,因为发生了死锁.我们在服务器上运行试试,没有输出,对应的进程是 32752. 使用 “jstack 32752”排查,后 ...
- day54 作业
编写代码,将当前日期按"2017-12-27 11:11 星期三"格式输出(提示:switch结构) var date = new Date() ymd = data.toLoca ...
- 关于echarts中的饼状图的label文字显示过长的问题
label: { normal: { fontSize: 14, formatter(v) { let text = v.name let count = text.indexOf('¥') cons ...
- 数据分析05 /pandas的高级操作
数据分析05 /pandas的高级操作 目录 数据分析05 /pandas的高级操作 1. 替换操作 2. 映射操作 3. 运算工具 4. 映射索引 / 更改之前索引 5. 排序实现的随机抽样/打乱表 ...
- python 生成器(三):生成器基础(三)生成器表达式
生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素.也就是说,如果列表推导是制造列表的工厂,那么生成器表达式就是制造生成器的工厂. 示例 14-8 先在 ...
- bzoj3384[Usaco2004 Nov]Apple Catching 接苹果*&&bzoj1750[Usaco2005 qua]Apple Catching*
bzoj3384[Usaco2004 Nov]Apple Catching 接苹果 bzoj1750[Usaco2005 qua]Apple Catching 题意: 两棵树,每分钟会从其中一棵树上掉 ...
- bzoj2456mode
bzoj2456mode 题意: 给你一个n个数的数列,求出现次数超过n div 2的数(只有1个). 题解: 注意空间只有1M,显然不能开数组.用两个变量,一个存“当前数”,另一个存“当前数”的个数 ...
- XML解析---利用XStream解析xml数据及反构造Java对象
XStream 是一个轻量级的.简单易用的开放源代码 Java库,用于将 Java 对象序列化为 XML 或者再转换回来.而且XStream还能将java对象转成其它格式,比如JSon. 需要用到的包 ...
- ant design pro : 依赖项 webpack-theme-color-replacer 最新版导致项目无法启动?
重新装了一个项目的依赖,结果发现打不开了? 报错如下: This dependency was not found: * webpack-theme-color-replacer/client in ...
- IDEA添加注释常用的快捷键
1.行注释Ctrl+/ 2.块注释Ctrl+Shift+/ 3.生成类注释 输入/**,然后按回车 (idea上没有生成类注释快捷键的,可以看这里 :idea生成类注释和方法注释的正确方法 ) 4.生 ...