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 NIO 实例之多人聊天
关键抽象 1.定义一个HashMap<String,SocketChannel>用户存储每个用户的管道. 2.服务端监听read事件,获取消息后轮询hashmap发送消息给用户模型内的所有 ...
- 一文搞懂Python函数(匿名函数、嵌套函数、闭包、装饰器)!
Python函数定义.匿名函数.嵌套函数.闭包.装饰器 目录 Python函数定义.匿名函数.嵌套函数.闭包.装饰器 函数核心理解 1. 函数定义 2. 嵌套函数 2.1 作用 2.2 函数变量作用域 ...
- python 迭代器(一):迭代器基础(一) 语言内部使用 iter(...) 内置函数处理可迭代对象的方式
简介 在 Python 中,所有集合都可以迭代.在 Python 语言内部,迭代器用于支持: 1.for 循环2.构建和扩展集合类型3.逐行遍历文本文件4.列表推导.字典推导和集合推导5.元组拆包6. ...
- 浏览器常见攻击方式(XSS和CSRF)
常见的浏览器攻击分为两种,一种为XSS(跨站脚本攻击),另一种则为CSRF(跨站请求伪造). XSS(跨站脚本攻击) 定义 XSS 全称是 Cross Site Scripting,为了与“CSS”区 ...
- Python 图像处理 OpenCV (14):图像金字塔
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...
- Java面试题汇总(持续更新)
1. ==和equals的区别 答: 基础数据类型比较:只能使用==,比较值是否相等 引用数据类型比较: 没有重写equals方法:==和equals没有区别,比较的都是引用是否指向了同一块内存 重写 ...
- C++语法小记---经典问题之一(一个空类包含什么)
问题:一个空类包含什么 空的构造函数 拷贝构造函数(浅拷贝) 重载赋值操作符函数(浅拷贝) 析构函数 取址运算符 取址运算符const 注意 所有的这些默认函数,只有在代码中调用了才会生成,否则也不会 ...
- 题解 洛谷 P4177 【[CEOI2008]order】
进行分析后,发现最大收益可以转化为最小代价,那么我们就可以考虑用最小割来解决这道题. 先算出总收益\(sum\),总收益减去最小代价即为答案. 然后考虑如何建图,如何建立最小割的模型. 发现一个任务最 ...
- 004.Nginx日志配置及状态监控
一 Nginx请求简介 1.1 请求头部 对于HTTP而言,客户端负责发起request请求,服务端负责response响应. request:包括请求行.请求头部.请求数据: response:包括 ...
- WPF入门教程(一)---基础
这篇主要讲WPF的开发基础,介绍了如何使用Visual Studio 2013创建一个WPF应用程序. 首先说一下学习WPF的基础知识: 1) 要会一门.NET所支持的编程语言.例如C#. 2) 会一 ...