在某些业务逻辑下,需要同时等待多个任务执行完成,才能继续往下执行后续逻辑。等待任务执行的逻辑,大部分情况下需要使用到 Task.WhenAll 方法,代码行数不少。另外,在需要获取多个异步任务的返回值的逻辑上,整体的逻辑代码量看起来也不少。本文将和大家介绍 TaskTupleAwaiter 库,通过 TaskTupleAwaiter 库可以方便等待多个任务执行完成,且方便获取各个异步任务的返回值

假定有两个异步任务方法,如以下代码,期望等待这两个方法执行完成,获取到结果,再执行后续逻辑

Task<string> GetFoo1Async() => Task.Run(() => "Foo1");

Task<string> GetFoo2Async() => Task.Run(() => "Foo2");

传统方法是通过 Task.WhenAll 等待任务完成,再获取对应的值,如以下代码

var task1 = GetFoo1Async();
var task2 = GetFoo2Async(); await Task.WhenAll(task1, task2); var (foo1, foo2) = (task1.Result, task2.Result);

但千万不要先等待第一个任务执行完成,再等待第二个任务执行完成哦,如果是如以下代码的写法,自然会没有充分利用资源,第二个任务还在等待中

var foo1 = await GetFoo1Async();
var foo2 = await GetFoo2Async();

在异步任务超过 3 个之后,代码逻辑的长度自然就很长了。接下来看看本文介绍的 TaskTupleAwaiter 库的优化后的写法

使用 TaskTupleAwaiter 库之后的可以简化为如下代码

var (foo1, foo2) = await (GetFoo1Async(), GetFoo2Async());

可以看到一行就实现上面大概用了 4 行才能完成的任务,随着异步任务的数量的增加,优化力度也会更加大,同时也能解决在返回值相同的时候,不小心写过等待的任务的坑

按照惯例,使用 TaskTupleAwaiter 库的第一步就是安装 NuGet 包,对于 SDK 格式的 csproj 项目文件,可以在 csproj 里面添加如下代码用来安装

  <ItemGroup>
<PackageReference Include="TaskTupleAwaiter" Version="2.0.0" />
</ItemGroup>

这个库的使用方法十分简单,只是创建一个扩展类,里面就对 ValueTuple 扩展了 GetAwaiter 方法,根据 C# await 高级用法 博客可以了解到,对于 await 等待来说,只需要等待的类型存在 GetAwaiter 方法且此 GetAwaiter 方法返回一个实现了等待相关方法的类型的对象即可

例如对于由三个 Task 任务组成的 ValueTuple 加上可等待的功能的扩展方法可以是如下代码

	public static TupleTaskAwaiter<T1, T2, T3> GetAwaiter<T1, T2, T3>(this (Task<T1>, Task<T2>, Task<T3>) tasks) =>
new(tasks); public readonly record struct TupleTaskAwaiter<T1, T2, T3> : ICriticalNotifyCompletion
{
private readonly (Task<T1>, Task<T2>, Task<T3>) _tasks;
private readonly TaskAwaiter _whenAllAwaiter; public TupleTaskAwaiter((Task<T1>, Task<T2>, Task<T3>) tasks)
{
_tasks = tasks;
_whenAllAwaiter = Task.WhenAll(tasks.Item1, tasks.Item2, tasks.Item3).GetAwaiter();
} public bool IsCompleted =>
_whenAllAwaiter.IsCompleted; public void OnCompleted(Action continuation) =>
_whenAllAwaiter.OnCompleted(continuation); [SecurityCritical]
public void UnsafeOnCompleted(Action continuation) =>
_whenAllAwaiter.UnsafeOnCompleted(continuation); public (T1, T2, T3) GetResult()
{
_whenAllAwaiter.GetResult();
return (_tasks.Item1.Result, _tasks.Item2.Result, _tasks.Item3.Result);
}
}

GetAwaiter 扩展方法,给 (Task<T1>, Task<T2>, Task<T3>) 扩展了可等待的方法,如此即可使用 await 进行等待

通过 TupleTaskAwaiter 实现具体的等待逻辑,核心实现依然是 Task.WhenAll 进行等待,只是对此进行封装

本文的代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 37c7473807581cde1215374856e5fd8f285c21a9

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 37c7473807581cde1215374856e5fd8f285c21a9

获取代码之后,进入 JahawciceyainalljoHeneeqearhi 文件夹

既然实现如此简单,那自然还有其他的库也可以实现相同的功能。例如 UniTask 库,这是一个支持在 Unity 更方便做异步的库,开源地址: https://github.com/Cysharp/UniTask

在非 Unity 下也依然可用,使用之后有两个可选写法,一个是 UniTask.WhenAll 等待方法,另一个是更加简洁的和 TupleTaskAwaiter 几乎完全相同的直接等待的方法,如以下的例子

    var task1 = GetTextAsync(UnityWebRequest.Get("http://google.com"));
var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com"));
var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com")); // 方法一是通过 UniTask.WhenAll 等待
// concurrent async-wait and get results easily by tuple syntax
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3); // 方法二是直接等待
// shorthand of WhenAll, tuple can await directly
var (google2, bing2, yahoo2) = await (task1, task2, task3);

dotnet 使用 TaskTupleAwaiter 同时等待多个任务简化代码写法的更多相关文章

  1. Selenium三种等待元素的方式及代码,需要特别注意implicitlyWait的用法

    一.显式等待 1.显式等待: 就是明确的要等到某个元素的出现或者是某个元素的可点击等条件,等不到,就一直等,除非在规定的时间之内都没找到,那么就跳出Exception. 2.代码: new WebDr ...

  2. Java线程的等待与唤醒完整示例代码

    项目结构: 资源类: 输入线程:  输出线程: 测试: 人妖问题发生: 线程安全问题的解决方法: 调用Object的wait()和notify()方法时需注意:必须是锁对象方可调用,否则将抛出无效的监 ...

  3. 关于InvokeRequired与Invoke

    from:http://www.th7.cn/Program/net/201306/140033.shtml Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性.因此,如果从另一个线程调 ...

  4. ASP.NET Core 中文文档 第二章 指南(8) 使用 dotnet watch 开发 ASP.NET Core 应用程序

    原文:Developing ASP.NET Core applications using dotnet watch 作者:Victor Hurdugaci 翻译:谢炀(Kiler) 校对:刘怡(Al ...

  5. 使用 dotnet watch 开发 ASP.NET Core 应用程序

    使用 dotnet watch 开发 ASP.NET Core 应用程序 原文:Developing ASP.NET Core applications using dotnet watch作者:Vi ...

  6. dotnet 替换 ASP.NET Core 的底层通讯为命名管道的 IPC 库

    这是一个用于本机多进程进行 IPC 通讯的库,此库的顶层 API 是采用 ASP.NET Core 的 MVC 框架,其底层通讯不是传统的走网络的方式,而是通过 dotnetCampus.Ipc 开源 ...

  7. dotnet 6 在 Win7 系统证书链错误导致 HttpWebRequest 内存泄露

    本文记录我将应用迁移到 dotnet 6 之后,在 Win7 系统上,因为使用 HttpWebRequest 访问一个本地服务,此本地服务开启 https 且证书链在此 Win7 系统上错误,导致应用 ...

  8. DotNet Run 命令介绍

    前言 本篇主要介绍 asp.net core 中,使用 dotnet tools 运行 dotnet run 之后的系统执行过程. 如果你觉得对你有帮助的话,不妨点个[推荐]. 目录 dotnet r ...

  9. .NET跨平台之旅:探秘 dotnet run 如何运行 .NET Core 应用程序

    自从用 dotnet run 成功运行第一个 "Hello world" .NET Core 应用程序后,一直有个好奇心:dotnet run 究竟是如何运行一个 .NET Cor ...

  10. dotnet tools 运行 dotnet run

    dotnet tools 运行 dotnet run dotnet run 命令介绍 前言 本篇主要介绍 asp.net core 中,使用 dotnet tools 运行 dotnet run 之后 ...

随机推荐

  1. 利用Python 去重聚合Excel数据并对比两份数据的差异

    问题背景 在数据处理过程中,常常需要将多个数据表进行合并,并进行比对,以便找出数据的差异和共同之处.本文将介绍如何使用 Pandas 库对两个 Excel 数据表进行合并与比对,并将结果输出到新的 E ...

  2. [javascript]细节与使用经验

    [版权声明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://www.cnblogs.com/cnb-yuchen/p/18031957 出自[进步*于辰的博客] 纯文字阐述,内 ...

  3. C# 平台调用过程

    (1)调用LoadLibrary加载非托管DLL到内存中,并调用GetProcAddress 获得内存中非托管函数的指针. (2)  为包含非托管函数地址的托管签名生成一个DllImport存根(st ...

  4. GFLV2:边界框不确定性的进一步融合,提点神器 | CVPR 2021

      GFLV2基于GFLV1的bbox分布进行改进,将分布的统计信息融入到定位质量估计中,整体思想十分创新和完备,从实验结果来看,效果还是挺不错的 来源:晓飞的算法工程笔记 公众号 论文: Gener ...

  5. KingbaseES Json 系列八:Json记录操作函数三

    KingbaseES Json 系列八--Json记录操作函数三(JSON_TABLE) JSON 数据类型是用来存储 JSON(JavaScript Object Notation)数据的.King ...

  6. Log4Net使用示例

    <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSe ...

  7. #费马小定理#JZOJ 4015 数列

    题目 给出\(x_n=(ax_{n-1}^2+bx_{n-1}+c)\bmod m\) 给出\(x_0.a,b,c,n,m\),求\(x_n\) \(\text{Subtask 1:}n\leq 10 ...

  8. 使用脚本整合指定文件/文件夹,执行定制化 ESLint 命令

    背景 最近面对一个庞大的项目,但是只需要修改某个模块,每次都手搓命令太麻烦了,于是就想着能不能写个脚本来辅助处理这些事情. 解决方案 定制化一键 ESLint,执行文件下载地址: https://gi ...

  9. Jetty的http2模块

    启用http2模块,执行如下命令: java -jar $JETTY_HOME/start.jar --add-modules=http2 命令的输出,如下: INFO : http2 initial ...

  10. 一文弄懂String的所有小秘密

    目录 简介 String是不可变的 传值还是传引用 substring() 导致的内存泄露 总结 简介 String是java中非常常用的一个对象类型.可以说java中使用最多的就是String了.那 ...