对于c#中的async和await的使用,没想到我一直竟然都有一个错误。。

。。还是总结太少,这里记录下。

这里以做早餐为例

流程如下:

  1. 倒一杯咖啡。
  2. 加热平底锅,然后煎两个鸡蛋。
  3. 煎三片培根。
  4. 烤两片面包。
  5. 在烤面包上加黄油和果酱。
  6. 倒一杯橙汁。

当使用同步方式实现时,代码是这样的:

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的使用。。的更多相关文章

  1. [译] C# 5.0 中的 Async 和 Await (整理中...)

    C# 5.0 中的 Async 和 Await [博主]反骨仔 [本文]http://www.cnblogs.com/liqingwen/p/6069062.html 伴随着 .NET 4.5 和 V ...

  2. ASP.NET 中的 Async/Await 简介

    本文转载自MSDN 作者:Stephen Cleary 原文地址:https://msdn.microsoft.com/en-us/magazine/dn802603.aspx 大多数有关 async ...

  3. [C#] .NET4.0中使用4.5中的 async/await 功能实现异

    好东西需要分享 原文出自:http://www.itnose.net/detail/6091186.html 在.NET Framework 4.5中添加了新的异步操作库,但是在.NET Framew ...

  4. 【TypeScript】如何在TypeScript中使用async/await,让你的代码更像C#。

    [TypeScript]如何在TypeScript中使用async/await,让你的代码更像C#. async/await 提到这个东西,大家应该都很熟悉.最出名的可能就是C#中的,但也有其它语言也 ...

  5. 在MVC中使用async和await的说明

    首先,在mvc中如果要用纯异步请不要使用async和await,可以直接使用Task.Run. 其次,在mvc中使用async和await可以让系统开新线程处理Task的代码,同时不必等Task执行结 ...

  6. JavaScript ES7 中使用 async/await 解决回调函数嵌套问题

    原文链接:http://aisk.me/using-async-await-to-avoid-callback-hell/ JavaScript 中最蛋疼的事情莫过于回调函数嵌套问题.以往在浏览器中, ...

  7. 在Silverlight中使用async/await

    现在 async/await 大行其道,确实,有了 async/await ,异步编程真是简单多了,个人觉得 async/await 的出现,给开发者还来的方便,绝不亚于当年 linq 的出现. 但要 ...

  8. 在现有代码中通过async/await实现并行

    在现有代码中通过async/await实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...

  9. Javascript中的async await

    async / await是Javascript是ES7的重要特性之一,也是目前社区里公认的优秀异步解决方案.目前,async / await这个特性已经是stage 3的建议,可以看看TC39的进度 ...

  10. async/await 的基本实现和 .NET Core 2.1 中相关性能提升

    前言 这篇文章的开头,笔者想多说两句,不过也是为了以后再也不多嘴这样的话. 在日常工作中,笔者接触得最多的开发工作仍然是在 .NET Core 平台上,当然因为团队领导的开放性和团队风格的多样性(这和 ...

随机推荐

  1. 持续集成工具Jenkins学习总结

    概述 持续集成(Continuous Integration,简称CI)是一种软件开发实践,团队开发人员每次都通过自动化的构建(编译.发布.自动化测试)来验证,从而尽早的发现集成错误.持续集成最大的优 ...

  2. 20135316王剑桥 linux第十周课实验笔记

    关于who 功能说明:显示目前登入系统的用户信息. 语 法:who [-Himqsw][--help][--version][am i][记录文件] 补充说明:执行这项指令可得知目前有那些用户登入系统 ...

  3. Redis集群服务器-高可用调研随笔[转]

    今天改了一天的Bug,本想下午开始专研Redis命令集,结果也泡汤了.只能在下班的路上考虑下Redis集群服务器的高可用方案.随笔而已,尚未成型,仅作记录. 当然,我说的可能比较片面,欢迎拍砖.斧正. ...

  4. mysql跟踪提交的SQL语句

    http://www.cnblogs.com/wuyifu/p/3328024.html

  5. C++读写文件流的相关介绍

    掌握文本文件读写的方法了解二进制文件的读写方法 C++文件流:fstream // 文件流ifstream  // 输入文件流ofstream  // 输出文件流 //创建一个文本文件并写入信息//同 ...

  6. ubuntu 搭建svn服务器

    1.安装Subversion sudo apt-get install subversion 2.创建资源库 cd /home/username/ svnserve -d -r /home/usern ...

  7. J2ee技术难点

    J2ee技术难点 session/cookie区别联系 jsp/servlet区别联系 filter执行流程 openSessionInView原理 clone与servilizable区别联系 eq ...

  8. MFC CDHtmlDialog 加载本地资源

    步骤:1.资源视图 项目右击选择资源添加,自定义添加新类型 如:JS(会增加JS文件夹)2. 选择1新建的文件夹右击 添加资源 导入 选择js文件引入3. 在资源文件Resource.h文件夹能找到资 ...

  9. 给iOS开发新手送点福利,简述UISlide的属性和用法

    UISlide属性 1.   minimumValue  : 当值可以改变时,滑块可以滑动到最小位置的值,默认为0.0 _slider.minimumValue = 10.0; 2.   maximu ...

  10. grub启动引导程序配置及命令行接口详解

    Grub(GRand Unified Bootloader),统一资源引导器,它的工作是提供一个菜单,允许用户选择要启动的系统或不同的内核版本:把用户选定的内核装载到RAM中的特定空间中,然后解压.展 ...