https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/index翻译

1. 引入

  Task异步编程模型(TAP)提供了对异步代码的抽象,将代码作为语句序列,可以在每个阶段完成下个阶段开始前读取代码,该过程中,编译器进行了多次转换,因为一些语句可能启动工作并返回正在进行的工作任务。

  Task异步编程的目标就是,启动类似于语句序列的代码,但当任务执行完成时,基于外部资源分配以一个更复杂的顺序执行任务,类似于人们如何为包含异步任务的进程发出指令。

2. 异步编程

  在本文中,通过一个制作早餐的示例,了解关键字async和await关键字如何使得包含一系列异步指令的操作更容易。

  制造早餐的列表如下:

  (1)倒一杯咖啡;

  (2)将锅加热,然后煎两个鸡蛋;

  (3)炒三片培根;

  (4)吐司两片面包;

  (5)加入黄油和果酱吐司;

  (6)倒一杯橙汁

  烹饪早餐是异步工作的一个很好范例,同一个人可以在一个步骤完成之前去执行另一个步骤。该操作的同步代码简易版如下:

static void Main(string[] args)
{
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!");
}

  如果采用上述给出的步骤进行早餐准备,整个效率会非常低下,而事实上,我们可以在锅加热煎鸡蛋的过程中,炒培根,在培根开始之后,就可以将面包放入烤面包机。要想实现动作的异步执行,需要编写异步代码。异步实现的简易代码如下:

static async void Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Egg eggs =await FryEggs();
Console.WriteLine("eggs are ready");
Bacon bacon =await FryBacon();
Console.WriteLine("bacon is ready");
Toast toast =await ToastBread();
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready"); Console.WriteLine("Breakfast is ready!");
}

  此时,煎鸡蛋、炒培根和烤面包这三个动作就不需要依次执行,当烹饪鸡蛋或培根时,代码不会阻止,可以同时启动多个组件任务。

2.1 同时启动任务

  许多情况下,我们希望立即启动多个独立任务,然后,当每个任务完成后,可以继续其他已准备好的工作。在上述早餐实例中,也就是要求更快的完成早餐。.NET Core中,System.Threading.Tasks.Task和相关类可以用来推理正在进行的任务类,该特性使得更容易编写接近实际创建早餐方式的代码。能够同时开始烹饪鸡蛋、培根和吐司。当每个动作需要执行时,我们可以把注意力转移到该任务上,注意下一个动作,然后等待其他需要注意的事情。

  我们可以启动一个任务并保留该工作的Task对象,await在处理结果之前,我们将完成每项任务。对上述创建早餐的代码进行修改,第一步是在操作开始时存储操作,而非等待它们。

Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Task<Egg> eggTask=FryEggs();
Egg eggs=await eggTask;
Console.WriteLine("eggs are ready");
Task<Bacon> baconTask=FryBacon();
Bacon bacon=await baconTask;
Console.WriteLine("bacon is ready");
Task<Toast> toastTask=ToastBread();
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> eggTask = FryEggs();
Task<Bacon> baconTask = FryBacon();
Task<Toast> toastTask = ToastBread();
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready"); Egg eggs = await eggTask;
Console.WriteLine("eggs are ready");
Task<Bacon> baconTask = FryBacon();
Bacon bacon = await baconTask;
Console.WriteLine("bacon is ready");

  该代码的效果更好,可以立即启动所有的异步任务,只有在需要结果时才等待每项任务。该代码的实现类似于web应用程序中的代码,能够发出不同微服务的请求,然后将结果组合成单个页面。此时,我们将立即发出所有的请求,然后await所有的任务并组合成web页面。

2.2 任务组合  

  上述制作早餐的过程中,制作吐司是异步操作(烤面包)和同步操作(添加黄油和果酱)的组合。此时,我们需要知道,异步操作和后续同步操作的组合是异步操作,即如果操作的任意部分是异步的,则整个操作都是异步的。

  下面给出创建工作组合的方法。在供应早餐之前,如果想要在添加黄油和果酱之前等待烘烤面包的任何,则可以使用以下代码表示:

async Task<Toast> makeToastWithButterAndJamAsync(int number){
var plainToast=await ToastBreadAsync(number);
ApplyButter(plainToast);
ApplyJsm(plainToast);
return plainToast;
}

  上述方法中包含了一个await语句,包含异步操作,该方法代表了烘烤面包的任务,然后添加黄油和果酱,之后返回一个Task<TResult>,表示这三个操作的组合结果。当前代码课修改为:

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!"); async Task<Toast> makeToastWithButterAndJamAsync(int number)
{
var plainToast = await ToastBreadAsync(number);
ApplyButter(plainToast);
ApplyJam(plainToast);
return plainToast;
} }

  以上代码的修改说明了异步代码工作的重要性,通过将操作分离为返回任务的新方法来组合任务,可以选择何时等待这项任务,同时启动其他任务

2.3 有效地等待其他任务

  await可以通过使用Task类的方法来该井前面代码末尾的一系列语句,其中一个API是WhenAll,它返回一个在其参数列表中所有任务完成时完成的Task,如以下代码所示:

await Task.WhenAll(eggTask,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>,我们在知道任务已经完成时,可以等待返回的结果。以下代码显示了如何使用WhenAny等待第一个任务完成然后处理其结果,处理完结果后,从传递给的任务列表中删除该已完成的任务。

var allTasks=new List<Task>{aggsTask,baconTask,toastTask};
while(allTask.Any()){
Task finished=await Task.WhenAny(allTasks);
if (finished == eggsTask)
{
Console.WriteLine("eggs are ready");
allTasks.Remove(eggsTask);
var eggs = await eggsTask;
} else if (finished == baconTask)
{
Console.WriteLine("bacon is ready");
allTasks.Remove(baconTask);
var bacon = await baconTask;
} else if (finished == toastTask)
{
Console.WriteLine("toast is ready");
allTasks.Remove(toastTask);
var toast = await toastTask;
} else
allTasks.Remove(finished);
}
Console.WriteLine("Breakfast is ready!");

  在所有更改后,最终版本main方法如下:

static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
var eggsTask = FryEggsAsync();
var baconTask = FryBaconAsync();
var toastTask = makeToastWithButterAndJamAsync();
var allTasks = new List<Task>{eggsTask, baconTask, toastTask};
while(allTask.Any()){
Task finished = await Task.WhenAny(allTasks);
if (finished == eggsTask)
{
Console.WriteLine("eggs are ready");
allTasks.Remove(eggsTask);
var eggs = await eggsTask;
} else if (finished == baconTask)
{
Console.WriteLine("bacon is ready");
allTasks.Remove(baconTask);
var bacon = await baconTask;
} else if (finished == toastTask)
{
Console.WriteLine("toast is ready");
allTasks.Remove(toastTask);
var toast = await toastTask;
} else
allTasks.Remove(finished);
}
Console.WriteLine("Breakfast is ready!"); async Task<Toast> makeToastWithButterAndJamAsync(int number)
{
var plainToast = await ToastBreadAsync(number);
ApplyButter(plainToast);
ApplyJam(plainToast);
return plainToast;
}
}

c#中的Task异步编程的更多相关文章

  1. Task异步编程

    Task异步编程中,可以实现在等待耗时任务的同时,执行不依赖于该耗时任务结果的其他同步任务,提高效率. 1.Task异步编程方法签名及返回值: a) 签名有async 修饰符 b) 方法名以 Asyn ...

  2. 新手浅谈C#Task异步编程

    Task是微软在.net framework 4.0发布的新的异步编程的利器,当然4.5新增了async.await,这儿我们先说Task相关. 在实际编程中,我们用的较多的是Task.Task.Fa ...

  3. 新手浅谈Task异步编程和Thread多线程编程

    初学Task的时候上网搜索,看到很多文章的标题都是task取代thread等等相关,我也一直以为task和thread是一类,其实task是.net4.0提出的异步编程,在之前.net1.0有dele ...

  4. Task 异步编程测试案例及基础应用说明

    对于多线程,我们经常使用的是Thread.在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为tas ...

  5. JS中的同步异步编程

    首先我们先看看同步与异步的定义,及浏览器的执行机制,方便我们更好地理解同步异步编程. 浏览器是多线程的,JS是单线程的(浏览器只分配一个线程来执行JS)   进程大线程小:一个进程中包含多个线程,例如 ...

  6. Play!中使用HTTP异步编程

    本章译者:@Sam Liu (译者未留下自己的主页,请Sam Liu见此文,加入群168013302联系‘大黄蜂@翻译play’) 这一章主要讲解如何运用异步模式实现典型的长连接(long-polli ...

  7. Task异步编程,刨根到底

    1. 编译器到底对await做了什么 await 一个异步操作的时候,实际上编译器会创建一个状态机,这个状态机包含了调用者的上下文变量,状态机使用yield迭代器实现,状态机由clr调度,每次运行都会 ...

  8. .NET 4.5 Task异步编程学习资料

    参考资料: 1. http://www.cnblogs.com/heyuquan/archive/2013/04/18/3028044.html

  9. .NET Web应用中为什么要使用async/await异步编程

    前言 什么是async/await? await和async是.NET Framework4.5框架.C#5.0语法里面出现的技术,目的是用于简化异步编程模型. async和await的关系? asy ...

随机推荐

  1. 012、Java中发生溢出的转换问题

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  2. 洛谷题解P1047 校门外的树

    题目描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,…,L,都种有 ...

  3. Most simple basic of internet programming based on two different machines sharing the same local net

    This blog is just shown for the most simple basic of internet programming based on two different mac ...

  4. JuJu团队12月2号工作汇报

    JuJu团队12月2号工作汇报 JuJu   Scrum 团队成员 今日工作 剩余任务 困难 于达 和婷婷一起调试main.jl 继续调试 金华实现的BiLSTM参数无法使用save存入 婷婷 和于达 ...

  5. ArcoLinux安装完成后的的配置

    ArcoLinux安装完成后的的配置 这可能是全网第一篇Arcolinux的教程 1. 更改源 修改/etc/pacman.d/mirrorlist 在最头上增加清华源 Server = https: ...

  6. htmp to pdf

    C++ Library to Convert HTML to PDF html2pdf PrinceXML 收费 CutePDF Ghostscript PDFDoc VisPDF PDFDoc Sc ...

  7. Machine Learning Note Phase 1( Done!)

    Machine Learning 这是第一份机器学习笔记,创建于2019年7月26日,完成于2019年8月2日. 该笔记包括如下部分: 引言(Introduction) 单变量线性回归(Linear ...

  8. 九十六、SAP中ALV事件之九,显示功能按钮栏中显示ALV加强工具栏

    一.排查了很久,终于找到问题所在.把问题解决了,代码如下: 二.运行效果如下 三.试一试,标准功能都可以用 完美

  9. 119-PHP调用private成员的方法

    <?php class ren{ //定义人类 private $birthday='1990-12-20'; //定义private修饰的成员属性 public function say_bi ...

  10. Redis Sentinel 学习笔记

    转载出处: http://blog.csdn.net/lihao21 概述 Redis Sentinel 是用来实现 Redis 高可用的一套解决方案.Redis Sentinel 由两个部分组成:由 ...