架构师_程序员_码农网

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5531|回复: 2

[交流] 【译】.NET 中的异步编程,使用 ValueTask 和 Task 的区别

[复制链接]
发表于 2022-3-17 11:21:50 | 显示全部楼层 |阅读模式
AsyncNET.png

使用 Task或Task类有一个我们在之前的文章中没有提到的性能瓶颈。简而言之,当结果立即可用时,这些类会导致不必要的分配。这意味着,即使结果已经可用, 也总是会创建一个新的Task或Task对象。现在,我们提到我们在之前的文章中使用的 async/await概念从 .NET 4.5 版本开始就已经存在。自 C# 7 以来,此功能已得到增强,即具有ValueTask结构的 .NET 4.7 版本,可以作为异步 函数的返回。

ValueTask 结构

ChMG6nc.png
ValueTask 结构于 2015 年首次出现在 corefxlab 存储库中。该存储库用于实验和探索可能会或可能不会进入主corefx存储库的新想法。Corefx 存储库是所有 .NET Core 基础库所在的存储库。它是由Stephen Taub 为 System.Threading.Tasks.Channels库开发和建议的。当时斯蒂芬提供了一个简短的解释:

corefxlab 库地址:https://github.com/dotnet/corefxlab


ValueTask是一个T和一个 Task 的有区别的并集,使得 ReadAsync 可以自由分配,以同步返回其可用的T值(与使用Task.FromResult不同,后者需要分配一个 Task 实例)。ValueTask 是可等待的,因此大多数实例的消耗与任务的消耗是无法区分的。
很多人看到了使用这种结构的好处 ,它作为 System.Threading.Tasks.Extensions  NuGet 包的一部分包含在 C#7 中。 因此,在我们深入研究 ValueTask 结构之前,让我们检查一下它用来解决的问题。由于 Task(Task) 是一个引用类型, 所以从异步方法返回 Task 对象意味着每次都在堆上分配它。这在许多情况下是必需的。

但是,在某些情况下, 异步方法会立即返回结果或同步完成。在这些情况下,这种分配是不必要的,并且可能在代码的性能关键部分变得昂贵。直到 .NET 4.7 版本,没有办法避免这种情况,因为异步方法必须返回 Task、  Task <T>或 void (最后一个通常不受欢迎)。在这个版本的 .NET 中,这是扩展的,这意味着异步方法可以返回任何类型,只要它具有可访问的 GetAwaiter 方法。 ValueTask 是这种类型的一个具体例子,它也被添加到这个版本中。

您可以浏览 corefx 存储库并查看 ValueTask 的完整实现,这里是我们感兴趣的 API 部分:



作为一个结构,ValueTask 允许编写在同步运行时不分配内存的异步方法。async/await 概念的 API 一致性并没有以这种方式受到损害。除此之外,这种结构可以自行等待,使其易于使用。例如,如果我们运行这个简单的代码:

在 MultiplyAsync方法中,我们正在模拟我们希望避免使用Task而只返回一个简单整数的情况。这是在方法的 if 语句中完成的,我们基本上是在检查传递的参数是否为零。问题是即使我们在if语句中的条件为真,上面的代码也会创建一个 Task 对象。我们这样解决这个问题:

ValueTask 和 Task 之间权衡

THXWf34.png

如前所述,使用 ValueTask有两个主要好处:

  • 性能改进
  • 提高实施灵活性


那么,性能改进背后的数字是多少?观察这段代码:


如果我们运行这段代码,执行 JIT 需要 120ns。现在,如果我们像这样用  ValueTask 替换 Task:

使用 JIT,我们将获得 65ns 的执行时间。现在,确实由于Task.Delay 我们没有同步执行,但我们看到执行时间有所改进。

我们提到的另一个好处是增加了实现的灵活性。现在,这到底是什么意思?好吧, 应该同步的异步接口的实现将被迫使用 Task.Run 或 Task.FromResult。当然,这会导致我们之前讨论过的性能问题。当我们使用 ValueTask 时,我们更有可能在同步或异步实现之间进行选择。请记住,这可能表明如果您遇到这种情况,您的代码可能设计得不好。

例如观察这个接口:


假设您想从这样的代码中调用它:

因为我们在接口中使用了 ValueTask,所以该接口的实现可以是同步的也可以是异步的。我们可以获得这个好处,基本上可以跳过向IThing 添加一些处理同步行为的函数。通过这种方式使用这个界面要容易得多。这是上面接口的同步实现:

这是同一接口的异步实现:

但是,在使用 ValueTask 之前,我们必须考虑一些权衡 。很容易认为应该默认使用 ValueTask 而不是 Task ,当然不是这样。例如,尽管 ValueTask 在结果同步可用的情况下帮助我们避免了不必要的分配,但它也包含两个字段。

重要的是要记住这是我们在这里使用的结构,这意味着我们使用值类型及其所有负担。另一方面,Task 是一个只有一个字段的引用类型 。当您使用 ValueTask 时,我们有更多的数据需要处理和处理。如果在异步方法中等待此类方法,则该异步方法的状态机也会更大,因为存储整个结构通常比存储单个引用需要更多空间。

这就是为什么微软的人实际上建议使用 Task 或 Task 作为异步方法的默认返回类型 。只有在性能分析之后,才应该考虑改用 ValueTask。


总结

ValueTask 是 .NET 4.7 中引入的一种结构,它为我们在 .NET 中使用异步方法提供了很多可能性。然而,它并非没有代价。这对于同步执行的性能关键方法非常有用。使用它们我们可以避免分配不必要的对象。尽管如此,作为一种值类型,它还是带有值类型通常具有的所有问题。因此,我们可以从这种结构中获益,但我们必须谨慎。

原文地址:https://rubikscode.net/2018/06/11/asynchronous-programming-in-net-benefits-and-tradeoffs-of-using-valuetask/




上一篇:【实战】使用 Docker 搭建 IPsec VPN 服务器
下一篇:【转】使用 OpenConnect 代替 Cisco AnyConnect,避免路由表锁定
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2022-3-18 22:21:57 | 显示全部楼层
又来学习学习。。
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2022-3-22 14:05:53 | 显示全部楼层
学习学习精华不错不错
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

免责声明:
码农网所发布的一切软件、编程资料或者文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:help@itsvse.com

QQ|手机版|小黑屋|架构师 ( 鲁ICP备14021824号-2 )|网站地图

GMT+8, 2024-4-24 13:36

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表