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实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...
随机推荐
- 三种存储方式DAS、NAS、SAN
------------恢复内容开始------------ 一.DAS.NAS.SAN在存储领域的位置 随着主机.磁盘.网络等技术的发展,数据存储的方式和架构也在一直不停改变,本文主要介绍目前主流的 ...
- vue中v-model父子组件通信
有这样的需求,父组件绑定v-model,子组件输入更改父组件v-model绑定的数值.是怎么实现的呢? 实际上v-model 只是语法糖而已. <input v-model="inpu ...
- ReadWriteLock锁的应用
对于 Lock 锁来说,如果要实现 "一写多读" 的并发状态(即允许同时读,不允许同时写),需要对 "写操作" 加锁,对 "读操作" 不作要 ...
- redis(十六):Redis 安装,部署(LINUX环境下)
第一步:下载安装包 访问https://redis.io/download 到官网进行下载.这里下载最新的4.0版本. 第二步:安装 1.通过远程管理工具,将压缩包拷贝到Linux服务器中,执行解压 ...
- LeetCode 82,考察你的基本功,在有序链表中删除重复元素II
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题的第51篇文章,我们来看LeetCode第82题,删除有序链表中的重复元素II(Remove Duplicates ...
- [Cordova]Cordova6.x自定义插件之Andorid
1.继承了CordovaPlugin的Java Class 需要重写execute方法,如下: 2.在res/xml/config.xml中关联上述java class 3.在assets/www/p ...
- java大数据最全课程学习笔记(3)--HDFS 简介及操作
目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages 目录 HDFS 简介及操作 HDFS概述 HDFS产出背景及定义 HDFS优缺点 HDFS组成架构 HDFS文件块大小 ...
- OSCP Learning Notes - Exploit(3)
Modifying Shellcode 1. Search “vulnserver exploit code” on the Internet. Find the following website ...
- 搭建高可用kubernetes集群(keepalived+haproxy)
序 由于单master节点的kubernetes集群,存在master节点异常之后无法继续使用的缺陷.本文参考网管流程搭建一套多master节点负载均衡的kubernetes集群.官网给出了两种拓扑结 ...
- 关于node-sass安装失败问题
在进行Vue开发中npm run dev报错,按照提示尝试进行npm以及cnpm安装均失败 解决办法:npm uninstall node-sass卸载模块 指定淘宝镜像源安装 npm i node- ...