c#中的Task异步编程
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异步编程的更多相关文章
- Task异步编程
Task异步编程中,可以实现在等待耗时任务的同时,执行不依赖于该耗时任务结果的其他同步任务,提高效率. 1.Task异步编程方法签名及返回值: a) 签名有async 修饰符 b) 方法名以 Asyn ...
- 新手浅谈C#Task异步编程
Task是微软在.net framework 4.0发布的新的异步编程的利器,当然4.5新增了async.await,这儿我们先说Task相关. 在实际编程中,我们用的较多的是Task.Task.Fa ...
- 新手浅谈Task异步编程和Thread多线程编程
初学Task的时候上网搜索,看到很多文章的标题都是task取代thread等等相关,我也一直以为task和thread是一类,其实task是.net4.0提出的异步编程,在之前.net1.0有dele ...
- Task 异步编程测试案例及基础应用说明
对于多线程,我们经常使用的是Thread.在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为tas ...
- JS中的同步异步编程
首先我们先看看同步与异步的定义,及浏览器的执行机制,方便我们更好地理解同步异步编程. 浏览器是多线程的,JS是单线程的(浏览器只分配一个线程来执行JS) 进程大线程小:一个进程中包含多个线程,例如 ...
- Play!中使用HTTP异步编程
本章译者:@Sam Liu (译者未留下自己的主页,请Sam Liu见此文,加入群168013302联系‘大黄蜂@翻译play’) 这一章主要讲解如何运用异步模式实现典型的长连接(long-polli ...
- Task异步编程,刨根到底
1. 编译器到底对await做了什么 await 一个异步操作的时候,实际上编译器会创建一个状态机,这个状态机包含了调用者的上下文变量,状态机使用yield迭代器实现,状态机由clr调度,每次运行都会 ...
- .NET 4.5 Task异步编程学习资料
参考资料: 1. http://www.cnblogs.com/heyuquan/archive/2013/04/18/3028044.html
- .NET Web应用中为什么要使用async/await异步编程
前言 什么是async/await? await和async是.NET Framework4.5框架.C#5.0语法里面出现的技术,目的是用于简化异步编程模型. async和await的关系? asy ...
随机推荐
- CVE-2019-0708—微软RDP远程桌面代码执行漏洞复现
0x01 2019年9月7日凌晨,msf上更新了0708的漏洞利用程序. 顿时安全群和朋友圈就爆炸了 - 奈何接到HW攻击队任务,又在家过了个中秋,0708才在今天更新. 0x02 环境 Window ...
- 和为S的连续正整数序列(双指针法)
题目描述 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久,他 ...
- 在 .net 中释放嵌入的资源
private static void ExtractResourceToFile(string resourceName, string filename) { if (!Syste ...
- mysql 模糊查询中包含特殊字符查询
- canvas绘制表盘时钟
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- python中excel表格的读写
#!usr/bin/env python #-*- coding:utf-8 -*- import xlrd import xlwt from xlutils.copy import copy imp ...
- Nature
1.主干 (1)<河图+洛书>:启发伏羲作八卦. (2)<三坟+五典>:失传:伏羲.神农.轩辕.少昊.颛顼.帝喾.唐尧.虞舜. (3)<八索+九丘>:失传:八卦之书 ...
- Java中默认方法
默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法,Mortal 这个接口,增加了一个默认方法 r,这个方法有实现体,并且被声明为了default,如以下代码: 这 ...
- 关于VMware vSphere Client安装时,.net framework4进度条卡住不动(亲测)
亲测有用的办法 1.点击电脑桌面右下角的"开始"按钮,点击"运行"按钮,在弹出的节目输入框中输入"regedit". 2.在弹出来的&quo ...
- Docker安装,基本概念,执行流程,生命周期简介
Docker基本概念 在使用Docker前,首先要先知道Docker中这几个常用的概念: 镜像:镜像是文件,只读的,提供了运行完整软硬件应用程序的集装箱. 容器:是镜像的实例,由Docker负责创建, ...