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 ...
随机推荐
- 016、Java中使用小数
01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...
- Linq----------if使用
static void Main(string[] args) { "; "; var processid = "c8b79051249940acbeca5dd951d2 ...
- 实验吧-web-Once More(php ereg()漏洞)!!!
题目:啊拉?又是php审计.已经想吐了. hint:ereg()函数有漏洞哩:从小老师就说要用科学的方法来算数. 给我们提示:1)ereg()函数漏洞 2)科学计数法 viewsource: < ...
- 09 MySQL字符集
字符集的选择 1.数据库方面最流行的是UTF-8 2.如果只考虑支持汉字,那么使用GBK,毕竟GBK下,每个汉字只占用2个字节,而UTF-8需要3个字节. 3.如果需要做大量的 ...
- B - Stacks of Flapjacks UVA - 120
BackgroundStacks and Queues are often considered the bread and butter of data structures and find us ...
- layui-注册界面
注册页面register.html源代码: <!DOCTYPE html> <html lang="en"> <head> <meta c ...
- JDK源码阅读-------自学笔记(一)(java.lang.Object重写toString源码)
一.前景提要 Object类中定义有public String toString()方法,其返回值是 String 类型. 二.默认返回组成 类名+@+16进制的hashcode,当使用打印方法打印的 ...
- 十一、SAP文本变量,并设置长度
一.在SAP中,一个中文占用2个文本长度,详见代码: 二.效果如下
- python 列表和字符串
python 列表中保留所有字符串前三项,并保存到一个新的列表l = [s[:3] for s in data] python 在列表中查找包含所以某个字符串的项,并保存到一个新的列表l = [s f ...
- bash: java: command not found
[root@izm5eab8t820b79js38tbxz ~]# java -version -bash: java: command not found 出现上面问题,解决方法: [root@iz ...