windows C#-异步编程场景(二)

等待多个任务完成

你可能发现自己处于需要并行检索多个数据部分的情况。 Task API 包含两种方法(即 Task.WhenAll 和 Task.WhenAny),这些方法允许你编写在多个后台作业中执行非阻止等待的异步代码。

此示例演示如何为一组 User 捕捉 userId 数据。

private static async Task<User> GetUserAsync(int userId)
{// Code omitted://// Given a user Id {userId}, retrieves a User object corresponding// to the entry in the database with {userId} as its Id.return await Task.FromResult(new User() { id = userId });
}private static async Task<IEnumerable<User>> GetUsersAsync(IEnumerable<int> userIds)
{var getUserTasks = new List<Task<User>>();foreach (int userId in userIds){getUserTasks.Add(GetUserAsync(userId));}return await Task.WhenAll(getUserTasks);
}

以下是另一种更简洁的使用 LINQ 进行编写的方法:

private static async Task<User[]> GetUsersAsyncByLINQ(IEnumerable<int> userIds)
{var getUserTasks = userIds.Select(id => GetUserAsync(id)).ToArray();return await Task.WhenAll(getUserTasks);
}

尽管它的代码较少,但在混合 LINQ 和异步代码时需要谨慎使用。 因为 LINQ 使用延迟的执行,因此异步调用将不会像在 foreach 循环中那样立刻发生,除非强制所生成的序列通过对 .ToList() 或 .ToArray() 的调用循环访问。 上述示例使用 Enumerable.ToArray 预先执行查询,并将结果存储在数组中。 这会强制代码 id => GetUserAsync(id) 运行并启动任务。

重要信息和建议

对于异步编程,有一些细节需要注意,以防止意外行为。

1. async 方法需要在主体中有 await 关键字,否则它们将永不暂停!

这一点需牢记在心。 如果 await 未用在 async 方法的主体中,C# 编译器将生成一个警告,但此代码将会以类似普通方法的方式进行编译和运行。 这种方式非常低效,因为由 C# 编译器为异步方法生成的状态机将不会完成任何任务。

2. 添加“Async”作为编写的每个异步方法名称的后缀。

这是 .NET 中的惯例,以便更为轻松地区分同步和异步方法。 未由代码显式调用的某些方法(如事件处理程序或 Web 控制器方法)并不一定适用。 由于它们未由代码显式调用,因此对其显式命名并不重要。

3. async void 应仅用于事件处理程序。

async void 是允许异步事件处理程序工作的唯一方法,因为事件不具有返回类型(因此无法利用 Task 和 Task<T>)。 其他任何对 async void 的使用都不遵循 TAP 模型,且可能存在一定使用难度,例如:

async void 方法中引发的异常无法在该方法外部被捕获。
async void 方法很难测试。
async void 方法可能会导致不良副作用(如果调用方不希望方法是异步的话)。
在 LINQ 表达式中使用异步 lambda 时请谨慎

LINQ 中的 Lambda 表达式使用延迟执行,这意味着代码可能在你并不希望结束的时候停止执行。 如果编写不正确,将阻塞任务引入其中时可能很容易导致死锁。 此外,此类异步代码嵌套可能会对推断代码的执行带来更多困难。 Async 和 LINQ 的功能都十分强大,但在结合使用两者时应尽可能小心。

4. 采用非阻止方式编写等待任务的代码

通过阻止当前线程来等待 Task 完成的方法可能导致死锁和已阻止的上下文线程,且可能需要更复杂的错误处理方法。 

0b8c7ab6208a4c3b966cee89efa35dd0.png

5. 如果可能,请考虑使用 ValueTask

从异步方法返回 Task 对象可能在某些路径中导致性能瓶颈。 Task 是引用类型,因此使用它意味着分配对象。 如果使用 async 修饰符声明的方法返回缓存结果或以同步方式完成,那么额外的分配在代码的性能关键部分可能要耗费相当长的时间。 如果这些分配发生在紧凑循环中,则成本会变高。 有关详细信息,请参阅通用的异步返回类型。

6. 考虑使用 ConfigureAwait(false)

常见的问题是“应何时使用 Task.ConfigureAwait(Boolean) 方法?”。 该方法允许 Task 实例配置其 awaiter。 这是一个重要的注意事项,如果设置不正确,可能会影响性能,甚至造成死锁。 

7. 编写状态欠缺的代码

请勿依赖全局对象的状态或某些方法的执行。 请仅依赖方法的返回值。 为什么?

这样更容易推断代码。
这样更容易测试代码。
混合异步和同步代码更简单。
通常可完全避免争用条件。
通过依赖返回值,协调异步代码可变得简单。
(好处)它非常适用于依赖关系注入。
建议的目标是实现代码中完整或接近完整的引用透明度。 这么做能获得可预测、可测试和可维护的代码库。

完整示例

下列代码是示例的 Program.cs 文件的完整文本。

using System.Text.RegularExpressions;
using System.Windows;
using Microsoft.AspNetCore.Mvc;class Button
{public Func<object, object, Task>? Clicked{get;internal set;}
}class DamageResult
{public int Damage{get { return 0; }}
}class User
{public bool isEnabled{get;set;}public int id{get;set;}
}public class Program
{private static readonly Button s_downloadButton = new();private static readonly Button s_calculateButton = new();private static readonly HttpClient s_httpClient = new();private static readonly IEnumerable<string> s_urlList = new string[]{"https://learn.microsoft.com","https://learn.microsoft.com/aspnet/core","https://learn.microsoft.com/azure","https://learn.microsoft.com/azure/devops","https://learn.microsoft.com/dotnet","https://learn.microsoft.com/dotnet/desktop/wpf/get-started/create-app-visual-studio","https://learn.microsoft.com/education","https://learn.microsoft.com/shows/net-core-101/what-is-net","https://learn.microsoft.com/enterprise-mobility-security","https://learn.microsoft.com/gaming","https://learn.microsoft.com/graph","https://learn.microsoft.com/microsoft-365","https://learn.microsoft.com/office","https://learn.microsoft.com/powershell","https://learn.microsoft.com/sql","https://learn.microsoft.com/surface","https://dotnetfoundation.org","https://learn.microsoft.com/visualstudio","https://learn.microsoft.com/windows","https://learn.microsoft.com/maui"};private static void Calculate(){// <PerformGameCalculation>static DamageResult CalculateDamageDone(){return new DamageResult(){// Code omitted://// Does an expensive calculation and returns// the result of that calculation.};}s_calculateButton.Clicked += async (o, e) =>{// This line will yield control to the UI while CalculateDamageDone()// performs its work. The UI thread is free to perform other work.var damageResult = await Task.Run(() => CalculateDamageDone());DisplayDamage(damageResult);};// </PerformGameCalculation>}private static void DisplayDamage(DamageResult damage){Console.WriteLine(damage.Damage);}private static void Download(string URL){// <UnblockingDownload>s_downloadButton.Clicked += async (o, e) =>{// This line will yield control to the UI as the request// from the web service is happening.//// The UI thread is now free to perform other work.var stringData = await s_httpClient.GetStringAsync(URL);DoSomethingWithData(stringData);};// </UnblockingDownload>}private static void DoSomethingWithData(object stringData){Console.WriteLine("Displaying data: ", stringData);}// <GetUsersForDataset>private static async Task<User> GetUserAsync(int userId){// Code omitted://// Given a user Id {userId}, retrieves a User object corresponding// to the entry in the database with {userId} as its Id.return await Task.FromResult(new User() { id = userId });}private static async Task<IEnumerable<User>> GetUsersAsync(IEnumerable<int> userIds){var getUserTasks = new List<Task<User>>();foreach (int userId in userIds){getUserTasks.Add(GetUserAsync(userId));}return await Task.WhenAll(getUserTasks);}// </GetUsersForDataset>// <GetUsersForDatasetByLINQ>private static async Task<User[]> GetUsersAsyncByLINQ(IEnumerable<int> userIds){var getUserTasks = userIds.Select(id => GetUserAsync(id)).ToArray();return await Task.WhenAll(getUserTasks);}// </GetUsersForDatasetByLINQ>// <ExtractDataFromNetwork>[HttpGet, Route("DotNetCount")]static public async Task<int> GetDotNetCount(string URL){// Suspends GetDotNetCount() to allow the caller (the web server)// to accept another request, rather than blocking on this one.var html = await s_httpClient.GetStringAsync(URL);return Regex.Matches(html, @"\.NET").Count;}// </ExtractDataFromNetwork>static async Task Main(){Console.WriteLine("Application started.");Console.WriteLine("Counting '.NET' phrase in websites...");int total = 0;foreach (string url in s_urlList){var result = await GetDotNetCount(url);Console.WriteLine($"{url}: {result}");total += result;}Console.WriteLine("Total: " + total);Console.WriteLine("Retrieving User objects with list of IDs...");IEnumerable<int> ids = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };var users = await GetUsersAsync(ids);foreach (User? user in users){Console.WriteLine($"{user.id}: isEnabled={user.isEnabled}");}Console.WriteLine("Application ending.");}
}// Example output:
//
// Application started.
// Counting '.NET' phrase in websites...
// https://learn.microsoft.com: 0
// https://learn.microsoft.com/aspnet/core: 57
// https://learn.microsoft.com/azure: 1
// https://learn.microsoft.com/azure/devops: 2
// https://learn.microsoft.com/dotnet: 83
// https://learn.microsoft.com/dotnet/desktop/wpf/get-started/create-app-visual-studio: 31
// https://learn.microsoft.com/education: 0
// https://learn.microsoft.com/shows/net-core-101/what-is-net: 42
// https://learn.microsoft.com/enterprise-mobility-security: 0
// https://learn.microsoft.com/gaming: 0
// https://learn.microsoft.com/graph: 0
// https://learn.microsoft.com/microsoft-365: 0
// https://learn.microsoft.com/office: 0
// https://learn.microsoft.com/powershell: 0
// https://learn.microsoft.com/sql: 0
// https://learn.microsoft.com/surface: 0
// https://dotnetfoundation.org: 16
// https://learn.microsoft.com/visualstudio: 0
// https://learn.microsoft.com/windows: 0
// https://learn.microsoft.com/maui: 6
// Total: 238
// Retrieving User objects with list of IDs...
// 1: isEnabled= False
// 2: isEnabled= False
// 3: isEnabled= False
// 4: isEnabled= False
// 5: isEnabled= False
// 6: isEnabled= False
// 7: isEnabled= False
// 8: isEnabled= False
// 9: isEnabled= False
// 0: isEnabled= False
// Application ending.

 

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

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

相关文章

web——sqliabs靶场——第九关——时间盲注

什么是时间盲注 时间盲注是指基于时间的盲注&#xff0c;也叫延时注入&#xff0c;根据页面的响应时间来判断是否存在注入。 使用sqlmap不同的技术 sqlmap --technique 参数用来设置具体SQL注入技术 B: Boolean-based blind 基于布尔的忙逐步 E:Error-based 报错注入 U&am…

Vue所有图片预加载加上Token请求头信息、图片请求加载鉴权

环境 Vue2、“axios”: “0.18.1”、webpack&#xff1a;“4.46.0”、ant-design-vue: “1.7.8” 描述 项目对安全要求比较高&#xff0c;所有后台返回的图片加载时都要加上token。比如资源图片&#xff0c;拍照打卡的图片&#xff0c;都需要鉴权。如果不带上token参数&…

此电脑中的百度网盘图标无法删除解决方法2024/11/18

教程很详细&#xff0c;直接上步骤 对于这种情况&#xff0c;修改注册表是很麻烦的&#xff0c;眨眼睛在这里推荐这位大佬的开源软件MyComputerManager 点击跳转MyComputerManager下载链接

【模型级联】YOLO-World与SAM2通过文本实现指定目标的零样本分割

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

Postman之变量操作

系列文章目录 Postman之变量操作 1.pm.globals全局变量2.pm.environment环境变量3.pm.collectionVariables集合变量4.pm.variables5.提取数据-设置变量-进行参数化串联使用 postman中分为全局变量、环境变量、集合变量、和普通变量 分别使用pm.globals、pm.environment、pm.co…

linux 常用命令指南(存储分区、存储挂载、docker迁移)

前言&#xff1a;由于目前机器存储空间不够&#xff0c;所以‘斥巨资’加了一块2T的机械硬盘&#xff0c;下面是对linux扩容的一系列操作&#xff0c;包含了磁盘空间的创建、删除&#xff1b;存储挂载&#xff1b;docker迁移&#xff1b;anaconda3迁移等。 一、存储分区 1.1 …

python读取Oracle库并生成API返回Json格式

一、安装必要的库 首先&#xff0c;确保已经安装了以下库&#xff1a; 有网模式 pip install flask pip install gevent pi install cx_Oracle离线模式&#xff1a; 下载地址&#xff1a;https://pypi.org/simple/flask/ # a. Flask Werkzeug-1.0.1-py2.py3-none-any.whl J…

Nature子刊 | 单细胞测序打开发育系统溯源新视角

神经系统是人体最为复杂且最为重要的器官之一。深入理解神经发育对于神经科学研究和再生医学具有举足轻重的作用。但神经元多样性的起源仍是一个亟待解决的难题。日益发展的单细胞测序技术让研究人员们有机会从细胞的异质性入手&#xff0c;对不同细胞类型之间的关联和分化路径…

5G CPE与4G CPE的主要区别有哪些

什么是CPE&#xff1f; CPE是Customer Premise Equipment&#xff08;客户前置设备&#xff09;的缩写&#xff0c;也可称为Customer-side Equipment、End-user Equipment或On-premises Equipment。CPE通常指的是位于用户或客户处的网络设备或终端设备&#xff0c;用于连接用户…

新增道路查询最短路径

一、问题描述 给你一个整数 n 和一个二维整数数组 queries。 有 n 个城市&#xff0c;编号从 0 到 n - 1。初始时&#xff0c;每个城市 i 都有一条单向道路通往城市 i 1&#xff08; 0 < i < n - 1&#xff09;。 queries[i] [ui, vi] 表示新建一条从城市 ui 到城市…

【数据结构】链表解析与实战运用(1.8w字超详细解析)

目录 引言 链表概念及结构 链表的优缺点 链表的分类 1.单向或者双向 2.带头或者不带头 3.带循环或者非循环 单链表接口函数的实现 接口函数一览 创建空节点&打印链表 尾部插入 头部插入 尾部删除 头部删除 查找 在pos位置之后插入节点 在pos位置之前插入节…

Python练习31

Python日常练习 题目&#xff1a; 分别输入两个整数以及一个加减乘除中的算术运算符&#xff0c;输出运算结果&#xff0c; 若输入其它运算符&#xff0c;则退出程序; 例如&#xff1a; 输出格式如下 【输入一个整数&#xff1a;】1 【输入另一个整数&#xff1a;】2 …

uniapp 自定义加载组件,全屏加载,局部加载 (微信小程序)

效果图 全屏加载 页面加载使用 局部加载 列表加载里面使用 使用gif html <template><view><view class"" v-if"typeFullScreen"><view class"loading" v-if"show"><view class""><i…

Mac 修改默认jdk版本

当前会话生效 这里演示将 Java 17 版本降低到 Java 8 查看已安装的 Java 版本&#xff1a; 在终端&#xff08;Terminal&#xff09;中运行以下命令&#xff0c;查看已安装的 Java 版本列表 /usr/libexec/java_home -V设置默认 Java 版本&#xff1a; 找到 Java 8 的安装路…

动态规划-最长公共子序列

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

nvm安装node遇到的若干问题(vscode找不到npm文件、环境变量配置混乱、npm安装包到D盘)

问题一&#xff1a;安装完nvm后需要做哪些环境变量的配置&#xff1f; 1.打开nvm文件夹下的setting文件&#xff0c;设置nvm路径和安装node路径&#xff0c;并添加镜像。 root: D:\software\nvm-node\nvm path: D:\software\nvm-node\nodejs node_mirror: https://npmmirror.c…

Zookeeper的简单使用Centos环境下

目录 前言 一、ZOokeeper是什么&#xff1f; 二、安装Zookeeper 1.进入官网下载 2.解压到服务器 3.配置文件 三.使用Zookeeper 3.1启动相关指令 3.2其他指令 3.3ACL权限 总结 前言 记录下安装zookeeper的一次经历 一、ZOokeeper是什么&#xff1f; ZooKeeper是一…

疫情中的图书馆管理:Spring Boot系统设计

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了疫情下图书馆管理系统的开发全过程。通过分析疫情下图书馆管理系统管理的不足&#xff0c;创建了一个计算机管理疫情下图书馆管理系统的方案。文章介绍了疫情下图…

Excel数据动态获取与映射

处理代码 动态映射 动态读取 excel 中的数据&#xff0c;并通过 json 配置 指定对应列的值映射到模板中的什么字段上 private void GetFreightFeeByExcel(string filePath) {// 文件名需要以快递公司命名 便于映射查询string fileName Path.GetFileNameWithoutExtension(fi…

网络(TCP)

目录 TCP socket API 详解 bind(): 我们的程序中对myaddr参数是这样初始化的: listen(): accept(): 理解accecpt的返回值: 饭店拉客例子 connect tcp服务器和udp类似的部分代码 把套接字设置为监听状态&#xff08;listen&#xff09; 测试 查看端口号和IP地址&…