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应用程序仍然只通过这个更改来响应用户。但是,对于这种情况,您需要更多。您不希望每个组件任务都按顺序执行。在等待前一个任务完成之前,最好先启动每个组件任务。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

log4j CVE-2021-44228 RCE漏洞复现

一、漏洞特征 Apache Log4j 是 Apache 的一个开源项目&#xff0c;Apache Log4j2是一个基于Java的日志记录工具。该工具重写了Log4j框架&#xff0c;并且引入了大量丰富的特性。我们可以控制日志信息输送的目的地为控制台、文件、GUI组件等&#xff0c;通过定义每一条日志信息的…

双编码器构建机器人零力拖动/导纳控制思路

前言 这篇博客主要记录昨日与实验室大佬针对UR5机器人拖动示教功能实现的思路。由于本人并非主攻力控方面。直到昨天在做实验的时候&#xff0c;与力控组的大佬讨论过后才了解UR机器人实现导纳控制的思路。 关于导纳控制/零力拖动 导纳控制与阻抗控制单从字面去理解很容易记…

多因素验证如何让企业邮箱系统登录更安全?

企业邮箱系统作为基础的办公软件之一&#xff0c;既是企业内外沟通的重要工具&#xff0c;也是连接企业多个办公平台的桥梁&#xff0c;往往涉及到客户隐私、业务信息、企业机密等等。为了保护邮箱账户的安全&#xff0c;设置登陆密码无疑是保护账户安全的常用措施之一。然而随…

如何设计一个网盘系统的架构

1. 概述 现代生活中已经离不开网盘&#xff0c;比如百度网盘。在使用网盘的过程中&#xff0c;有没有想过它是如何工作的&#xff1f;在本文中&#xff0c;我们将讨论如何设计像百度网盘这样的系统的基础架构。 2. 系统需求 2.1. 功能性需求 用户能够上传照片/文件。用户能…

一题三解(暴力、二分查找算法、单指针):鸡蛋掉落

涉及知识点 暴力、二分查找算法、单指针 题目 给你 k 枚相同的鸡蛋&#xff0c;并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。 已知存在楼层 f &#xff0c;满足 0 < f < n &#xff0c;任何从 高于 f 的楼层落下的鸡蛋都会碎&#xff0c;从 f 楼层或比它低的…

归并分治 归并排序的应用 + 图解 + 笔记

归并分治 前置知识&#xff1a;讲解021-归并排序 归并排序 图解 递归 非递归 笔记-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/134338789?spm1001.2014.3001.5501原理&#xff1a; (1&#xff09;思考一个问题在大范围上的答案&#xff0c;是否等于&…

postman 参数化使用csv导入外部数据

一、参数化脚本入参 postman中变量用{{变量名}}表示变量 二、创建外部数据文件 csv文件逗号分割多个变量和对应值注意编码格式必须为utf-8 三、run collection导入数据文件 四、设置运行参数run 浏览数据 可调试设置迭代次数&#xff1a;防止批量出错&#xff0c;可先设定…

新风机小助手-风压变速器

风压变速器是一种用于调节系统中风量和风压的装置&#xff0c;常用于通风系统中。它通过改变进出风口的开度来调整风流的速度和风压。 风压变速器通常由进出风口和可调节的风门组成。风门可以手动或自动调节&#xff0c;控制进出风口的开度&#xff0c;从而改变风量和风压。根据…

uni-app基于vite和vue3创建并集成pinia实现数据持久化

一、uni-app基于Vite和Vue3创建并集成pinia实现数据持久化 文章目录 一、uni-app基于Vite和Vue3创建并集成pinia实现数据持久化1.如何创建基于Vite和Vue3的uni-app项目&#xff1f;2.选择其中一个分支&#xff0c;就是一个脚手架 二、以下都是基于vite-ts版本创建和配置1.目录结…

第一章 Object-XML 映射简介

文章目录 第一章 Object-XML 映射简介基础如何工作的映射选项IRIS 中的相关工具XML 文档的可能应用 第一章 Object-XML 映射简介 基础 将对象映射到 XML 一词意味着定义如何将该对象用作 XML 文档。要将对象映射到 XML&#xff0c;请将 %XML.Adaptor 添加到定义该对象的类的超…

线性代数-Python-04:线性系统+高斯消元的实现

文章目录 1 线性系统2 高斯-jordon消元法的实现2.1 Matrix2.2 Vector2.3 线性系统 3 行最简形式4 线性方程组的结构5 线性方程组-通用高斯消元的实现5.1 global5.2 Vector-引入is_zero5.3 LinearSystem5.4 main 1 线性系统 2 高斯-jordon消元法的实现 2.1 Matrix from .Vecto…

搭建完全分布式Hadoop

文章目录 一、Hadoop集群规划二、在主节点上配置Hadoop&#xff08;一&#xff09;登录虚拟机&#xff08;二&#xff09;设置主机名&#xff08;三&#xff09;主机名与IP地址映射&#xff08;四&#xff09;关闭与禁用防火墙&#xff08;五&#xff09;配置免密登录&#xff…

根据DataFrame指定的列该列中如果有n个不同元素则将其转化为n行显示explode()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 根据DataFrame指定的列 该列中如果有n个不同元素 则将其转化为n行显示 explode() 选择题 以下代码两次输出结果分别为几行&#xff1f; import pandas as pd df pd.DataFrame({种类:[蔬菜,水…

什么是 CASB,在网络安全中的作用

数字化转型正在稳步攀升&#xff0c;组织现在越来越关注在线生产力系统和协作平台&#xff0c;各行各业的企业都采用了不同的云基础设施服务模式。云基础架构提供按需服务&#xff0c;可提高易用性、访问控制、内容协作和减少内部存储资源&#xff0c;以及许多其他好处。迁移到…

【Mysql】查询mysql的版本

目录 cmd命令查询 mysql -- help(命令&#xff09; mysql -u root -p(命令&#xff09; 数据库管理工具查询 select version(); cmd命令查询 mysql -- help(命令&#xff09; mysql -u root -p(命令&#xff09; 执行该命令并且输入数据库密码 数据库管理工具查询 selec…

DAY50 309.最佳买卖股票时机含冷冻期 + 714.买卖股票的最佳时机含手续费

309.最佳买卖股票时机含冷冻期 题目要求&#xff1a;给定一个整数数组&#xff0c;其中第 i 个元素代表了第 i 天的股票价格 。 设计一个算法计算出最大利润。在满足以下约束条件下&#xff0c;你可以尽可能地完成更多的交易&#xff08;多次买卖一支股票&#xff09;: 你不…

如何解决msvcr90.dll文件丢失,电脑丢失msvcr90.dll什么意思

在计算机使用过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;比如“msvcr90.dll丢失”。这是因为msvcr90.dll是Microsoft Visual C 2008运行库的一部分&#xff0c;当这个文件丢失或者损坏时&#xff0c;就会导致程序无法正常运行。那么&#xff0c;如何解决这个问题…

麒麟V10系统下编译libcef_dll

目录 前言1、下载cef2、编译libcef_dll2.1 问题一 cmake版本太低2.2 问题二 无法识别的编译选项 -m64 3、总结 前言 本篇主要记录了在飞腾PC麒麟V10系统下编译libcef_dll时遇到的问题及解决方法。在Qt应用程序中使用QWebEngine加载HTML网页算是常规操作&#xff0c;但是涉及到…

Clickhouse SQL

insert insert操作和mysql一致 标准语法&#xff1a;insert into [table_name] values(…),(….)从表到表的插入&#xff1a;insert into [table_name] select a,b,c from [table_name_2] update 和 delete ClickHouse 提供了 Delete 和 Update 的能力&#xff0c;这类操作…

[100天算法】-最短无序连续子数组(day 70)

题目描述 给定一个整数数组&#xff0c;你需要寻找一个连续的子数组&#xff0c;如果对这个子数组进行升序排序&#xff0c;那么整个数组都会变为升序排序。你找到的子数组应是最短的&#xff0c;请输出它的长度。示例 1:输入: [2, 6, 4, 8, 10, 9, 15] 输出: 5 解释: 你只需要…