C# 图解教程 第5版 —— 第21章 异步编程

news2024/11/16 21:24:48

文章目录

    • 21.1 什么是 异步
    • 21.2 async/await 特性的结构
    • 21.3 什么是异步方法
      • 21.3.1 异步方法的控制流
      • 21.3.2 取消一个异步操作
      • 21.3.3 在调用方法中同步地等待任务
      • 21.3.4 在异步方法中异步地等待任务
      • 21.3.5 Task.Delay 方法
    • 21.4 GUI 程序中的异步操作(*)
    • 21.5 使用异步 Lambda 表达式(*)
    • 21.6 一个完整的 GUI 示例
    • 21.7 BackgroundWorker 类(*)
    • 21.8 并行循环(*)
    • 21.9 其他异步编程模式(*)
    • 21.10 BeginInvoke 和 EndInvoke(*)
    • 21.11 计时器(*)

21.1 什么是 异步

​ 启动程序时,系统会在内存中创建一个新的进程。在进程内部,系统创建了一个称为线程的内核对象,代表真正执行的程序(线程是“执行线程”的简称)。一旦进程建立,系统会在 Main 方法的第一行语句处开始线程的执行

  • 进程是构成运行程序的资源集合,包括虚地址空间、文件句柄和运行程序所需的其他东西。
  • 默认情况下,一个进程只包含一个线程,从程序的开始一直执行到结束。
  • 线程可以派生其他线程,因此任意时刻,一个进程都可能包含不同状态的多个线程,执行程序的不同部分。
  • 同一个进程的多个线程共享进程的资源。
  • 系统为处理器执行所调度的单元是线程,而不是进程。

​ 典型异步例子:

  1. 服务器程序接收来自多个客户端程序的服务请求。
  2. 交互式 GUI 程序,如果用户启动了需要消耗大量时间的操作,那么在程序完成前,用户仍能在屏幕上移动窗口,甚至可以取消操作。

示例

  1. 创建 Stopwatch 类的一个实例并启动,用于测量代码中不同任务的执行时间。
  2. 调用 CountCharacters 方法 2 次,下载某网站的内容,返回网站包含的字符数。
  3. 调用 CountToALargeNumber 方法 4 次,该方法仅执行一个消耗一定时间的任务,并循环指定次数。
  4. 最后,打印两个网站的字符数。
image-20231225215026247 image-20231225215111207

​ 某次代码运行生成的结果如下所示,Call 1 和 Call 2 占用了大部分时间,绝大部分时间都浪费在等待网站的响应上。

image-20231225215431983 image-20231225215511444
图 21.1 程序中不同任务所需时间的时间轴

​ 如果能发起 2 个 CountCharacter 调用,无需等待结果,就可以显著提升性能。

  1. 当 DoRun 调用 CountCharactersAsync 时,CountCharactersAsync 将立即返回,然后才真正开始下载字符。该方法返回 Tast<int> 类型的占位符对象,表示计划进行的工作,这个占位符最终将“返回”一个 int。
  2. 执行两次 CountCharactersAsync 方法后,调用 4 次 CountToALargeNumber,同时 CountCharactersAsync 的两次调用继续它们的工作——基本上是等待。
  3. DoRun 的最后两行 从 CountCharactersAsync 调用返回的 Tasks 中获取结果,如果还没有结果,将阻塞并等待。
image-20231225220923206 image-20231225221000137

​ 某次运行结果如下,新版程序比旧版快 32%,因为 CountToALargeNumber 的 4 次调用都是在 CountCharactersAsync 方法调用等待网站响应的时候进行的。所有这些工作都是在主线程中完成的,没有创建任何额外的线程。

image-20231225221034597 image-20231225221129452
图 21.2 async/await 版本的程序的时间轴

21.2 async/await 特性的结构

​ 该特性由如下 3 个部分组成:

  1. 调用方法。
  2. 异步方法。
  3. await 表达式。
image-20240109111217158
图 21.3 async/await 特性的整体结构

21.3 什么是异步方法

​ 异步的方法在完成其所有工作之前就返回到调用方法,然后在调用方法继续执行的时候完成其工作。其特点如下:

  1. 方法中包含 async 方法修饰符。

  2. 包含一个或多个 await 表达式,表示可以异步完成的任务。

  3. 返回类型必须是以下 3 种或不返回(void)。其中 Task 和 Task<T> 的返回对象表示将在未来完成的工作,调用方法和异步方法可以继续执行。

    • Task

      如果调用方法不需要从异步方法中返回某个值,但需要检查异步方法的状态,就可以返回该类型的对象。如果异步方法中有 return,则不能返回任何对象。

      image-20240109112137845
    • Task<T>

      如果调用方法需要从调用中获取类型 T 的值,异步方法的返回类型就必须是 Task<T>。调用方法将通过读取 Task 的 Result 属性来获取该值。

      image-20240109112253209
    • ValueTask<T>

      与 Task<T> 类似,但用于任务结果可能已经可用的情况。由于是值类型,因此可以放在栈上,无需像 Task<T> 对象那样在堆上分配空间。因此,某些情况下可以提高性能。

  4. 任何任何具有公开可访问的 GetAwaiter 方法的类型。

  5. 异步方法的形参不能为 out 或 ref 参数。

  6. 异步方法的名称应该以 Async 后缀结尾。

  7. Lambda 表达式和匿名方法也可以作为异步对象。

image-20240109111159224
图 21.4 异步方法的结构

​ 有关 async 的说明:

  • 异步方法的方法头中必须包含 async 关键字,且必须位于返回类型之前。
  • async 只起到标识作用,表示方法中包含一个或多个 await 表达式,本身不能创建任何异步操作。
  • async 是上下文关键字,可以在其他区域用作标识符。

21.3.1 异步方法的控制流

​ 异步方法的结构包含 3 个不同的区域:

  1. 第一个 await 表达式之前的部分。

    应该只包含少量无需长时间处理的代码。

  2. await 表达式。

    表示将被异步执行的代码。

  3. 后续部分。

    包括执行环境(所在线程信息、目前作用域内的变量值等)以及所需的其他信息。

image-20240109112849837
图 21.5 异步方法中的代码区域

​ 从第一个 await 表达式之前的代码开始同步执行,直到遇见第一个 await。当 await 的任务完成时,方法继续同步执行。如果还有其他 await,则重复上述步骤。

image-20240109113115258
图 21.6 贯穿一个异步方法的控制流

​ 当到达 await 表达式时,异步方法将控制返回到调用方法。如果方法的返回类型为 Task 或 Task<T>,则方法将创建一个 Task 对象,表示需一步完成的任务和后续,然后将该 Task 返回到调用方法。因此会产生 2 个控制流:异步方法和调用方法。

​ 异步方法内的代码会完成如下工作:

  1. 异步执行 await 表达式的空闲任务。
  2. 当 await 表达式完成时,执行后续部分。后续部分也可能包含其他 await 表达式,也将按照相同的步骤处理。
  3. 遇到 return 语句或到达方法末尾时:
    • 如果方法返回 void,控制流将退出。
    • 如果方法返回 Task,则将设置 Task 的状态属性并退出。
    • 如果方法返回 Task<T> 或 ValueTask<T>,则同时设置 Task 的状态属性和 Result 属性,再退出。

​ 同时地,调用方法中的代码将继续其任务,从异步方法中获取 Task<T> 或 ValueTask<T> 对象。当需要实际值时,就引用其 Result 属性。此时,如果异步方法设置了该属性,调用方法就能获得该值并继续;否则将暂停并等待该属性被设置,然后再继续执行。

​ 因此,异步方法中的 return 语句“返回”一个结果或到达末尾时,它并没有真正地返回某个值——它只是退出了。

await 表达式

​ await 表达式指定了一个异步执行的任务,由 await 关键字和一个空闲对象(称为任务,可能是 Task 类型,也可能不是)组成。默认情况下,这个任务在当前线程上异步执行。

image-20240109114402463

​ 一个空闲对象即是 awaitable 类型的实例。awaitable 类型包含了 GetAwaiter 方法,该方法没有参数,返回一个 awaiter 类型的对象。awaiter 类型包含如下成员:

image-20240109114534127

​ 同时,包含以下成员之一:

image-20240109114600625

​ 实际上,不需要构建自己的 awaitable,而是使用 Task 类或 ValueTask 类,它们是 awaitable 类型。通过 Task.Run 方法来创建 Task,其签名如下。其中 Func<TReturn> 是一个预定义委托(见第 20 章),不包含任何参数,返回类型为 TReturn。

image-20240109114905084

​ 下面的实例展示了具体使用方法。

  • a 使用 Get10 创建名为 ten 的 Func<int> 委托。
  • b 在 Task.Run 方法的参数列表中创建 Func<int> 委托。
  • c 使用 Lambda 表达式并隐式转化为 Func<int> 委托。
image-20240110154447289 image-20240110154521441

​ 表21.1 列出了Task.Run 方法的 8 个重载,表 21.2 展示了可能用到的 4 个委托类型的签名。

表 21.1 Task.Run 重载的返回类型和签名
image-20240110154802718
表 21.2 可作为 Task.Run 方法第一个参数的委托类型
image-20240110154853535

​ 4 种委托类型对应的使用方法展示如下:

image-20240110155946961 image-20240110155959089

21.3.2 取消一个异步操作

​ System.Threading.Tasks 命名空间中有两个类被设计为取消异步操作,分别为 CancellationToken 和 CancellationTokenSource。

  • CancellationToken 包含一个任务是否应被取消的信息 IsCancellationRequested。
  • 拥有 CancellationToken 对象的任务需要定期检查其令牌状态,如果 IsCancellationRequested 属性为 true,任务需停止其操作并返回。
  • CancellationToken 不可逆,且只能使用一次。即,一旦 IsCancellationRequested 被设置为 true,就不能更改。
  • CancellationTokenSource 对象创建可分配给不同任务的 CancellationToken 对象,任何持有 CancellationTokenSource 的对象都可以调用其 Cancel 方法,这将使 IsCancellationRequested 被设置为 true。
image-20240110160458050

​ 第一次运行时,保留注释代码,不会取消任务:

image-20240110160832360

​ 第二次运行将注释代码取消,则任务将在 3 s 后停止:

image-20240110160908305

异常处理和 await 表达式

image-20240110160943773 image-20240110160953537

​ 注意,尽管 Task 抛出了 Exception,但在 Main 的最后,Task 的状态仍然为 RanToCompletion。原因如下:

  1. Task 没有被取消。
  2. 没有未处理的异常。(IsFaulted 为 False 也是因为这个原因)

​ C#6.0 后可以在 catch 和 finally 块中使用 await 表达式。在异常不需要终止应用程序时,可以使用 await 来记录日志或运行其他时间较长的任务。如果新的异步任务也产生了一场,则任何原有的异常信息都将丢失。

21.3.3 在调用方法中同步地等待任务

​ 使用 Wait 方法可以等待 Task 对象完成。

image-20240110162930583 image-20240110163013407

​ 使用 Task 类中的静态方法等待一组 Task 对象完成。

  • Task.WaitAll
  • Task.WaitAny
image-20240110164104353 image-20240110164137902 image-20240110164304798

​ 只取消第一行注释,结果如下。

image-20240110164332814 image-20240110164343091

​ 只取消第二行注释,结果如下。

image-20240110164414643 image-20240110164423894

​ Task.WaitAll 和 Task.WaitAny 的其他重载方法如表 21.3 所示。

表 21.3 Task.WaitAll 和 Task.WaitAny 的重载方法
image-20240110164441174

21.3.4 在异步方法中异步地等待任务

​ 使用 Task.WhenAll 和 Task.WhenAny 异步等待多个 Task。

image-20240110164855240 image-20240110164939247 image-20240110164956414

​ 将 Task.WhenAll 替换为 Task.WhenAny 后,输出结果如下。

image-20240110165030105

21.3.5 Task.Delay 方法

​ Task.Delay 方法创建一个 Task 对象,该对象将暂停其在线程中的处理,并在一定时间后完成。

  • Thread.Sleep:阻塞线程。
  • Task.Delay:不阻塞线程,线程可以继续处理其他工作。
image-20240110165216224 image-20240110165225129

​ Delay 方法包含 4 个重载,允许以不同方式来指定时间周期,并允许使用 CancellationToken 对象。

表 21.4 Task.Delay 方法的重载
image-20240110165457101

21.4 GUI 程序中的异步操作(*)

21.5 使用异步 Lambda 表达式(*)

21.6 一个完整的 GUI 示例

21.7 BackgroundWorker 类(*)

21.8 并行循环(*)

21.9 其他异步编程模式(*)

21.10 BeginInvoke 和 EndInvoke(*)

21.11 计时器(*)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

倒L天线设计

λ/4单极子天线具有工作带宽较宽&#xff0c;辐射效率较高的优点&#xff0c;但是其体积较大&#xff0c;随着无线终端设备的体积越来越小&#xff0c;对天线空间的要求也越来越严格&#xff0c;于是为了适应终端设备的发展&#xff0c;单极子天线开始出现一些变形&#xff0c;…

基于Python实现身份证信息识别

目录 前言身份证信息识别的背景与意义自动识别身份证的需求 实现环境与工具准备Python编程语言OpenCV图像处理库Tesseract OCR引擎 身份证信息识别算法原理图像预处理步骤(图像裁剪、灰度化 、二值化、去噪)信息提取与解析 Python代码实现通过OCR提取身份证号码代码解析身份证信…

SQL-修改数据

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

计算机毕业设计 | SSM 校园线上订餐系统(附源码)

1&#xff0c; 概述 1.1 项目背景 传统的外卖方式就是打电话预定&#xff0c;然而&#xff0c;在这种方式中&#xff0c;顾客往往通过餐厅散发的传单来获取餐厅的相关信息&#xff0c;通过电话来传达自己的订单信息&#xff0c;餐厅方面通过电话接受订单后&#xff0c;一般通…

【博士每天一篇论文-综述】Brain Inspired Computing : A Systematic Survey and Future Trends

阅读时间&#xff1a;2023-11-17 1 介绍 年份&#xff1a;2023 作者&#xff1a;李国琪 期刊&#xff1a;TechRxiv 引用量&#xff1a;2 这篇论文主要介绍了脑启发计算&#xff08;Brain Inspired Computing&#xff0c;BIC&#xff09;以及其在人工智能&#xff08;Artifici…

MySQL安装服务启动失败解决方案

在安装MySQL中&#xff0c;应用配置阶段&#xff0c;显示服务启动失败 查看日志说服务启动失败 我的电脑是win764位 新装的操作系统&#xff0c;之前出现过权限不足的提示&#xff0c;首先定位故障为权限问题。由于MySQL80服务在 计算机管理->服务 里面显示户别为&#xff…

ubuntu20固定串口名称

查看串口的详细信息 udevadm info --name/dev/ttyUSB0结果&#xff1a; P: /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.2/1-1.2:1.0/ttyUSB0/tty/ttyUSB0 N: ttyUSB0 L: 0 S: serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UAR…

机器人持续学习基准LIBERO系列4——robosuite最基本demo

0.前置 机器人持续学习基准LIBERO系列1——基本介绍与安装测试机器人持续学习基准LIBERO系列2——路径与基准基本信息机器人持续学习基准LIBERO系列3——相机画面可视化及单步移动更新 1.robosuite的相关资料 是基于MuJoCo的机器人学习方针环境&#xff0c;提供一套基准环境…

2024.1.8 Day04_SparkCore_homeWork

目录 1. 简述Spark持久化中缓存和checkpoint检查点的区别 2 . 如何使用缓存和检查点? 3 . 代码题 浏览器Nginx案例 先进行数据清洗,做后续需求用 1、需求一&#xff1a;点击最多的前10个网站域名 2、需求二&#xff1a;用户最喜欢点击的页面排序TOP10 3、需求三&#x…

2024年最好用的简历编辑工具,助你腾飞职业生涯!

随着科技的不断发展&#xff0c;求职竞争也愈发激烈。在2024年&#xff0c;如何在众多求职者中脱颖而出成为关键问题。为了帮助大家在职业生涯中取得更好的机会&#xff0c;特别推荐一款在2024年最为出色的简历编辑工具——芊芊简历。 1. 创新的编辑功能 芊芊简历拥有直观易用…

Matlab 使用 DH table 建立的 robot 和实际不符

机器人仿真 想借助 matlab robotics toolbox 来仿真机器人&#xff0c;但是直接输入自己的 DH table 显示出来的 robot 和实际不情况不符。 DH table 建立 robot Build Manipulator Robot Using Kinematic DH Parameters 主要使用 setFixedTransform&#xff0c;DH table 中…

YOLOV7剪枝流程

YOLOV7剪枝流程 1、训练 1&#xff09;划分数据集进行训练前的准备&#xff0c;按正常的划分流程即可 2&#xff09;修改train.py文件 第一次处在参数列表里添加剪枝的参数&#xff0c;正常训练时设置为False&#xff0c;剪枝后微调时设置为True parser.add_argument(--pr…

POI:对Excel的基本写操作 整理1

首先导入相关依赖 <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --><!--xls(03)--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.2</version></depend…

鸿蒙Harmony--状态管理器-@Observed装饰器和@ObjectLink装饰器详解

经历的越多&#xff0c;越喜欢简单的生活&#xff0c;干净的东西&#xff0c;清楚的感觉&#xff0c;有结果的事&#xff0c;和说到做到的人。把圈子变小&#xff0c;把语放缓&#xff0c;把心放宽&#xff0c;用心做好手边的事儿&#xff0c;该有的总会有的! 目录 一&#xff…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)在TcpConnection 中接收并解析Http请求消息

一、在TcpConnection 中多添加和http协议相关的request和response struct TcpConnection {struct EventLoop* evLoop;struct Channel* channel;struct Buffer* readBuf;struct Buffer* writeBuf;char name[32];// http协议struct HttpRequest* request;struct HttpResponse* r…

leecode1143 | 最长公共子序列

给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff08;也可以不…

第7章 PKI 和密码应用

7.1 非对称密码 第6章的“现代密码学”一节介绍了私钥&#xff08;对称&#xff09;和公钥&#xff08;非对称&#xff09;密码的基本原则。 你曾学过&#xff0c;对称密钥密码系统要求通信双方使用同一个共享秘密密钥&#xff0c;因而形成了安全分发密钥的问题。 你还曾学过…

如何使用科大讯飞星火大模型AI批量生成文章

如何使用科大讯飞的星火大模型AI工具批量生成文章呢&#xff1f; 我们可以使用科大讯飞AI的星火大模型API接口&#xff0c;它支持批量处理和生成文章的AI功能。 但是星火大模型API接口无法直接使用&#xff0c;一般需要技术人员开发对应程序对接才行。为了让不懂技术的普通用…

【微信小程序开发】深入学习小程序开发之功能扩展和优化

前言 随着移动互联网的快速发展&#xff0c;微信小程序作为一种轻量级应用&#xff0c;已经逐渐成为许多企业和个人进行业务推广和服务提供的重要平台本文将详细介绍 微信小程序开发的功能扩展和优化&#xff0c;帮助开发者更好地提升小程序的用户体验和性能。 一、功能扩展 …

数据库系统概念 第七版 中文答案 第3章 SQL介绍

3.1 将以下查询使用SQL语言编写&#xff0c;使用大学数据库模式。 &#xff08;我们建议您实际在数据库上运行这些查询&#xff0c;使用我们在书籍网站db-book.com上提供的示例数据。有关设置数据库和加载示例数据的说明&#xff0c;请参阅上述网站。&#xff09; a. 查找计算机…