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. LCA之tarjan离线

    显然81篇题解是有点多了,不让我提交. 更为不好的是没有一篇详细的\(tarjan\)(不过我也不会写详细的). 不过\(tarjan\)并没有我们想象的那样难理解,时间也并不爆炸(巧妙的跳过难写二字 ...

  2. 利用 vuex 实现一个公用搜索器

    安装 npm i vuex vuex 的使用 先创建好如图所示的文件: 编写 modules 下的 params.js const param = { state: { params: {} }, m ...

  3. Python连载61-tkinter三种布局

    一.pack布局举例 #pack布局案例 import tkinter baseFrame = tkinter.Tk() #以下代码都是创建一个组件,然后布局 btn1 = tkinter.Butto ...

  4. .net 4.0 : Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.Binder.****'

    解决办法,添加 MicroSoft.CSharp 的引用.

  5. MQTT 协议学习:004-MQTT建立通信与 CONNECT 、CONNACK 报文

    背景 上一讲 MQTT 协议学习:通信报文的构成介绍了在MQTT通信中,各报文的通信流程:从本讲开始,我们开始介绍实际中使用的报文,以及它们的组成. CONNECT - 连接请求 报文 客户端到服务端 ...

  6. scanf与正则表达式的搭配及应用

    scanf与正则表达式的搭配及应用 正则其实我也学的不咋地,只会一点皮毛,正则最大的作用就是当输入流是一个字符串,我们能在输入的时候就滤掉无用信息,省去后期提取数值的步骤. 正则的语法我怕误人子弟,嘿 ...

  7. VS2013+HALCON13

    HALCON安装与配置(VS2013+HALCON13) 2017-06-23 16:08:25 坚强的羊脂球 阅读数 4574更多 分类专栏: HALCON   配置主要分为三部分: 1)VS调用H ...

  8. cf 762C. Two strings

    因为要删去1个串(读错题),所以就直接二分搞就好了. 需要预处理出2个分别从头到尾,或从尾到头需要多长a串的数组,然后二分删去多长就好了. #include<bits/stdc++.h> ...

  9. 云时代架构阅读笔记六——Java内存模型详解(二)

    承接上文:云时代架构阅读笔记五——Java内存模型详解(一) 原子性.可见性.有序性 Java内存模型围绕着并发过程中如何处理原子性.可见性和有序性这三个特征来建立的,来逐个看一下: 1.原子性(At ...

  10. 十三、JavaScript之跨多行的变量申明

    一.代码如下 二.运行效果 <!DOCTYPE html> <html> <meta http-equiv="Content-Type" conten ...