C# 同步异步大白话

同步异步大白话

背景

任务异步编程模型(TAP)提供了对异步代码的抽象。您可以像往常一样,将代码编写为一系列语句。您可以阅读该代码,就好像每条语句都在下一条语句开始之前完成一样。编译器执行许多转换,因为其中一些语句可能开始工作并返回表示正在进行的工作的Task

这就是该语法的目标:启用读起来像一系列语句的代码,但根据外部资源分配和任务完成时,代码的执行顺序要复杂得多。这类似于人们如何为包含异步任务的流程提供指令。在本文中,您将使用一个制作早餐的指令示例来了解async和wait关键字如何使您更容易推理包含一系列异步指令的代码。你可以写下类似以下列表的说明来解释如何做早餐:

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

如果你有烹饪经验,你会异步执行这些指令。你应该先把锅里的鸡蛋加热,然后再开始培根。你应该把面包放进烤面包机,然后开始煮鸡蛋。在这个过程的每一步,你都会开始一项任务,然后把注意力转移到准备好引起你注意的任务上。

烹饪早餐是异步工作的一个很好的例子。一个人(或线程)可以处理所有这些任务。继续早餐的类比,一个人可以在第一个任务完成之前开始下一个任务,异步地做早餐。不管有没有人在看,烹饪都在进行。一旦你开始加热锅里的鸡蛋,你就可以开始煎培根了。一旦培根开始烤,你就可以把面包放进烤面包机了。

对于并行算法,您需要多个厨师(或线程)。一个做鸡蛋,一个做培根,等等。每一个都只专注于一项任务。每个厨师(或线程)都会被同步阻塞,等待培根准备翻转或烤面包爆裂。

让我们从更新这段代码开始,这样线程在任务运行时就不会阻塞。await关键字提供了一种非阻塞的方式来启动任务,然后在任务完成后继续执行。

在这里插入图片描述

在这里插入图片描述

using System;
using System.Threading.Tasks;namespace AsyncBreakfast
{// These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.internal class Bacon { }internal class Coffee { }internal class Egg { }internal class Juice { }internal class Toast { }class Program{static void Main(string[] args){Coffee cup = PourCoffee();Console.WriteLine("coffee is ready");Egg eggs = FryEggs(2);Console.WriteLine("eggs are ready");Bacon bacon = FryBacon(3);Console.WriteLine("bacon is ready");Toast toast = ToastBread(2);ApplyButter(toast);ApplyJam(toast);Console.WriteLine("toast is ready");Juice oj = PourOJ();Console.WriteLine("oj is ready");Console.WriteLine("Breakfast is ready!");}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 = 0; slice < slices; slice++){Console.WriteLine("Putting a slice of bread in the toaster");}Console.WriteLine("Start toasting...");Task.Delay(3000).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(3000).Wait();for (int slice = 0; slice < slices; slice++){Console.WriteLine("flipping a slice of bacon");}Console.WriteLine("cooking the second side of bacon...");Task.Delay(3000).Wait();Console.WriteLine("Put bacon on plate");return new Bacon();}private static Egg FryEggs(int howMany){Console.WriteLine("Warming the egg pan...");Task.Delay(3000).Wait();Console.WriteLine($"cracking {howMany} eggs");Console.WriteLine("cooking the eggs ...");Task.Delay(3000).Wait();Console.WriteLine("Put eggs on plate");return new Egg();}private static Coffee PourCoffee(){Console.WriteLine("Pouring coffee");return new Coffee();}}
}

不要阻塞,等待

前面的代码演示了一种糟糕的做法:构造同步代码来执行异步操作。正如所写的,这段代码会阻止执行它的线程执行任何其他工作。当任何任务正在进行时,它都不会被中断。就好像你把面包放进去后盯着烤面包机看一样。你会忽略任何和你说话的人,直到烤面包爆裂。

让我们从更新这段代码开始,这样线程在任务运行时就不会阻塞。await关键字提供了一种非阻塞的方式来启动任务,然后在任务完成后继续执行。生成早餐代码的一个简单异步版本如下所示:

static async Task Main(string[] args)
{Coffee cup = PourCoffee();Console.WriteLine("coffee is ready");Egg eggs = await FryEggsAsync(2);Console.WriteLine("eggs are ready");Bacon bacon = await FryBaconAsync(3);Console.WriteLine("bacon is ready");Toast toast = await ToastBreadAsync(2);ApplyButter(toast);ApplyJam(toast);Console.WriteLine("toast is ready");Juice oj = PourOJ();Console.WriteLine("oj is ready");Console.WriteLine("Breakfast is ready!");
}

当鸡蛋或培根烹饪时,此代码不会被阻止。不过,此代码不会启动任何其他任务。你仍然会把烤面包放在烤面包机里,盯着它看,直到它爆裂。但至少,你会回应任何想引起你注意的人。在一家有多份订单的餐厅里,厨师可以在第一份正在烹饪的时候开始另一份早餐。

同时启动任务 Start tasks concurrently

在许多情况下,您希望立即启动几个独立的任务。然后,随着每项任务的完成,您可以继续其他已准备好的工作。在早餐的比喻中,这就是你如何更快地完成早餐的方法。你还可以在几乎同一时间完成所有事情。你会得到一顿热早餐。

System.Threading.Tasks.Task和相关类型是可用于推理正在进行的任务的类。这使您能够编写与创建早餐更相似的代码。你应该同时开始煮鸡蛋、培根和烤面包。由于每一项都需要行动,你会把注意力转向那项任务,做好下一项行动,然后等待其他需要你注意的事情。

您启动一个任务并抓住代表该工作的task对象。您将等待每个任务,然后再处理其结果。

让我们对早餐代码进行这些更改。第一步是在操作开始时存储任务,而不是等待它们:

Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");Task<Egg> eggsTask = FryEggsAsync(2);
Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");Task<Bacon> baconTask = FryBaconAsync(3);
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");Task<Toast> toastTask = ToastBreadAsync(2);
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!");

在这里插入图片描述
异步准备的早餐的最终版本大约需要6分钟,因为有些任务是并发运行的,并且代码同时监视多个任务,并且只在需要时才采取行动。

最后的代码是异步的。它更准确地反映了一个人如何做早餐。将前面的代码与本文中的第一个代码示例进行比较。通过阅读代码,核心操作仍然清晰可见。您可以像阅读本文开头的早餐制作说明一样阅读此代码。async和await的语言功能为每个人提供了遵循这些书面指令的翻译:尽可能启动任务,不要阻止等待任务完成。

在这里插入图片描述

任务组成 Composition with tasks

除了烤面包,你早餐的所有东西都同时准备好了。
烤面包是由异步操作(烤面包)和同步操作(添加黄油和果酱)组成的。更新此代码说明了一个重要概念:

重要的
异步操作和同步工作的组合是异步操作。换句话说,如果操作的任何部分是异步的,那么整个操作就是异步的。

前面的代码向您展示了可以使用TaskTask<TResult>对象来保存正在运行的任务。您等待每个任务,然后再使用其结果。下一步是创建表示其他工作组合的方法。在供应早餐之前,您需要等待代表在添加黄油和果酱之前烤面包的任务。您可以使用以下代码来表示该工作:

static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{var toast = await ToastBreadAsync(number);ApplyButter(toast);ApplyJam(toast);return toast;
}

前面的更改说明了使用异步代码的一项重要技术。通过将操作分离到返回任务的新方法中,可以组合任务。您可以选择何时等待该任务。您可以同时启动其他任务。

高效地等待任务

前面代码末尾的一系列等待语句可以通过使用Task类的方法进行改进。

  • 其中一个API是WhenAll,它返回一个在其参数列表中的所有任务都已完成时完成的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>,当它的任何参数完成时,它就完成了。

您可以等待返回的任务,知道它已经完成。下面的代码显示了如何使用WhenAny来等待第一个任务完成,然后处理其结果。处理完已完成任务的结果后,将从传递给WhenAny的任务列表中删除该已完成任务。

var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{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");}await finishedTask;breakfastTasks.Remove(finishedTask);
}

现在,在等待任何尚未完成的已启动任务时,处理早餐的线程不会被阻塞。对于某些应用程序,只需要进行此更改。GUI应用程序仍然只通过这个更改来响应用户。但是,对于这种情况,您需要更多。您不希望每个组件任务都按顺序执行。在等待前一个任务完成之前,最好先启动每个组件任务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/187247.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VulnHub Nullbyte

一、信息收集 1.nmap扫描 arp-scan -l扫描内网存活主机 ┌──(root&#x1f480;kali)-[~/桌面] └─# nmap -sS -A -p- 192.168.103.201/24 -sS 半扫描 -A 扫描详细信息 -p- 扫描全端口发现开放了80、111、777、50978端口 且发现777端口开放了ssh服务&#xff0c;说明他把…

十个使用Spring Cloud和Java创建微服务的实践案例

在使用Java构建微服务时&#xff0c;许多人认为只要学习一些微服务设计模式就足够了&#xff0c;比如CQRS、SAGA或每个微服务一个数据库。虽然这是正确的&#xff0c;但同时学习一些通用的最佳实践也是很有意义的。本文分享一些最佳实践。 1 设计模块化的微服务 微服务应该专…

计算机技术专业CSIT883系统分析与项目管理介绍

文章目录 前言一、学科学习成果二、使用步骤三、最低出勤要求四、讲座时间表五、项目管理 前言 本课程介绍了信息系统开发中的技术和技术&#xff0c;以及与管理信息技术项目的任务相关的方法和过程。 它研究了系统分析师、客户和用户在系统开发生命周期中的互补角色。 它涵盖…

弹性布局display:flex

弹性布局display:flex 一、弹性布局的特点二、容器的属性1、justify-content1.1 justify-content: center 居中1.2 justify-content: flex-start&#xff08;默认值&#xff09;&#xff1a;左对齐1.3 justify-content: flex-end 右对齐1.4 justify-content:space-between 两端…

黑马程序员微服务SpringCloud实用篇02

SpringCloud实用篇02 0.学习目标 1.Nacos配置管理 Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&#xff0c;逐个修改微服务配置就会让人抓狂&#xff0c;而且很容易出错。我…

查找或替换excel换行符ctrl+j和word中的换行符^p,^l

一、excel中 直接上图。使用ctrlh调出替换&#xff0c;查找内容里按ctrlj&#xff08;会出现一个闪的小点&#xff09;&#xff0c;即为换行符。 二、word中 在word中&#xff0c;^p和^l分别代表换行符&#xff08;enter&#xff09;和手动换行符&#xff08;使用shiftenter&…

亚马逊云科技Zero ETL集成全面可用,可运行近乎实时的分析和机器学习

亚马逊云科技数据库、数据分析和机器学习全球副总裁Swami Sivasubramanian曾指出&#xff1a;“数据是应用、流程和商业决策的核心。”如今&#xff0c;客户常用的数据传输模式是建立从Amazon Aurora到Amazon Redshift的数据管道。这些解决方案能够帮助客户获得新的见解&#x…

Win10专业版安装wsl-ubuntu子系统

文章目录 一、查看是否满足安装要求二、管理员权限启动 Windows PowerShell三、启用Windows10子系统功能四、启用虚拟机平台功能五、重启电脑六、下载 Linux 内核更新包&#xff08;适用于 x64 计算机的 WSL2 Linux 内核更新包&#xff09;七、将 WSL 2 设置为默认版本八、打开…

华为gre带验证key案例

配置FW_A。 a.配置接口的IP地址&#xff0c;并将接口加入安全区域。 system-view [sysname] sysname FW_A [FW_A] interface GigabitEthernet 1/0/1 [FW_A-GigabitEthernet1/0/1] ip address 1.1.1.1 24 [FW_A-GigabitEthernet1/0/1] quit [FW_A] interface GigabitEthernet 1/…

Python数据容器(字符串)

字符串 1.字符串 字符串也是数据容器的一种&#xff0c;字符串是字符的容器&#xff0c;一个字符串可以存放任意数量的字符。 2.字符串的下标索引 从前向后&#xff0c;下标从0开始从后向前&#xff0c;下标从-1开始 # 通过下标索引获取特定位置的字符 name python print(na…

【技术类-01】doc转PDF程序卡死的解决方案,

摘要&#xff1a; 1、报错&#xff1a; raise AttributeError("%s.%s" % (self._username_, attr))&#xff09; 2、表现&#xff1a;doc转PDF卡死&#xff08;白条不动或出现以上英文&#xff09; 3、解决&#xff1a;在docx保存代码行后面加上time.sleep(3) 4、…

计算机是如何进行工作的+进程和线程

一)计算机是如何工作的? 指令是如何执行的?CPU基本工作过程&#xff1f; 假设上面有一些指令表&#xff0c;假设CPU上面有两个寄存器A的编号是00&#xff0c;B的编号是01 1)第一个指令0010 1010&#xff0c;这个指令的意思就是说把1010地址上面的数据给他读取到A寄存器里面 2…

ubuntu 16.04.5 安装 vivado 2019.1 完整编译AD9361的环境

一、前期安装 1、安装ncurses库&#xff08;已经包含了&#xff0c;其他的os需要安装&#xff09; sudo apt install libncurses5二、安装 sudo ./xsetup使用lic进行激活。 三、安装后 输入指令 sudo gedit ~/.bashrc 末尾添加 source /opt/Xilinx/Vivado/2019.1/setti…

生态环境领域基于R语言piecewiseSEM结构方程模型

结构方程模型&#xff08;Sructural Equation Modeling&#xff0c;SEM&#xff09;可分析系统内变量间的相互关系&#xff0c;并通过图形化方式清晰展示系统中多变量因果关系网&#xff0c;具有强大的数据分析功能和广泛的适用性&#xff0c;是近年来生态、进化、环境、地学、…

ruoyi前后端分离版本开发框架解读---让你快速入门

后端结构 com.ruoyi ├── common // 工具类 │ └── annotation // 自定义注解 │ └── config // 全局配置 │ └── constant // 通用常量 │ └── core …

10. GPIO中断

10. GPIO中断 回顾stm32中断系统STM32中断向量表中断向量偏移NVIC中断控制器 Cortex_A7 中断系统中断向量表GIC控制器中断IDGIC逻辑分块CP15协处理器c0寄存器c1寄存器c12寄存器c15寄存器 中断使能中断优先级设置优先级数配置 GICC_PMR抢占优先级和子优先级位数设置 GICC_BPR优先…

Vue中的 配置项 setup

setup 是 Vue3 中的一个全新的配置项&#xff0c;值为一个函数。 setup 是所有 Composition API&#xff08;组合式API&#xff09;的入口&#xff0c;是 Vue3 语法的基础。 组件中所用到的数据、方法、计算属性等&#xff0c;都需要配置在 setup 中。 setup 会在 beforeCre…

使用WinDbg分析CPU100%的问题

在我们软件运行的时候&#xff0c;偶尔会出现CPU占比100%的问题&#xff0c;而且极其不容易排查&#xff0c;概率极低&#xff0c;我硬是操作了一个下午&#xff0c;出现了一次&#xff0c;然后找到了dmp文件&#xff0c;也没有任何的规律&#xff0c;那么就可以借助windbg进行…

好用的vscode插件

一、代码管理 git GitLens — Git supercharged Git History gitignore 项目管理 Project Manager 管理多个项目 Todo Tree 快速定位代码中的todo WakaTime 用于在编程活动中自动统计工作量、代码提交和时间跟踪等 VS Code Counter 该插件用于帮助我们统计项目代码的行数…

Android ConstraintLayout

0dp 充满约束 to左侧是控件本身,to右边是约束条件 例如: app:layout_constraintBottom_toBottomOf"id/image_1" app:layout_constraintTop_toBottomOf"id/image_1" 指定权重: app:layout_constraintHorizontal_weight"" app:layout_constra…