【C# Task】 ValueTask/Task<TResult>
概要
1、如果异步方法的使用者使用 Task.WhenAll 或 Task.WhenAny,则在异步方法中使用 ValueTask<T> 作为返回类型可能会产生高昂的成本。这是因为您需要使用 AsTask 方法将 ValueTask<T> 转换为 Task<T>这将产生一个分配,如果首先使用了缓存的 Task<T>,则可以轻松避免这种分配
2、每个值任务只能使用一次。此处的"消费"一词意味着 ValueTask 可以异步等待(等待)操作完成,或者利用 AsTask 将 ValueTask 转换为任务。但是,一个值任务只应使用一次,之后应忽略值任务<T>。
如何在 C 中使用 ValueTask#
利用 C# 中的 ValueTask,避免在从异步方法返回任务对象时进行分配
异步编程已经使用了相当长一段时间。近年来,随着异步和等待关键字的引入,它变得更加强大。您可以利用异步编程来提高应用程序的响应能力和吞吐量。
C# 中异步方法的建议返回类型是 Task。如果要编写返回值的异步方法,则应返回 Task<T>。如果要编写事件处理程序,可以改为返回 void。在 C# 7.0 之前,异步方法可以返回 Task、Task<T> 或 void。从 C# 7.0 开始,异步方法还可以返回 ValueTask(作为 System.Threading.Tasks.Extensions 包的一部分提供)或 ValueTask<T>。本文讨论了如何在 C# 中使用 ValueTask。
为什么我应该使用 ValueTask?
任务表示某个操作的状态,即操作是否已完成、是否取消等。异步方法可以返回 Task 或 ValueTask。
现在,由于 Task 是引用类型,因此从异步方法返回 Task 对象意味着每次调用该方法时都要在托管堆上分配该对象。因此,使用 Task 时需要注意的一点是,每次从方法返回 Task 对象时,都需要在托管堆中分配内存。如果方法正在执行的操作的结果立即可用或同步完成,则不需要此分配,因此成本会变得高昂。
这正是ValueTask的救星。ValueTask<T>提供了两个主要优点。首先,ValueTask<T>提高了性能,因为它不需要堆分配,其次,它既简单又灵活地实现。当结果立即可用时,通过从异步方法返回 ValueTask<T> 而不是 Task<T>,可以避免不必要的分配开销,因为此处的"T"表示结构,而 C# 中的结构是值类型(与 Task<T> 中的"T"相反,后者表示类)。
Task 和 ValueTask 表示 C# 中的两种主要的"可等待"类型。请注意,您无法阻止值任务。如果需要阻止,则应使用 AsTask 方法将 ValueTask 转换为任务,然后阻止该引用 Task 对象。
另请注意,每个值任务只能使用一次。此处的"消费"一词意味着 ValueTask 可以异步等待(等待)操作完成,或者利用 AsTask 将 ValueTask 转换为任务。但是,一个值任务只应使用一次,之后应忽略值任务<T>。
C# 中的值任务示例
假设您有一个返回 Task 的异步方法。您可以利用 Task.FromResult 创建 Task 对象,如下面给出的代码片段所示。
- public Task<int> GetCustomerIdAsync()
- {
- return Task.FromResult(1);
- }
- 上面的代码片段不会创建整个异步状态机魔术,但它会在托管堆中分配一个 Task 对象。若要避免此分配,您可能希望改为利用 ValueTask,如下面给出的代码段所示。
- public ValueTask<int> GetCustomerIdAsync()
- {
- return new ValueTask(1);
- }
以下代码段阐释了 ValueTask 的同步实现。
- public interface IRepository<T>
- {
- ValueTask<T> GetData();
- }
存储库类扩展 IRepository 接口并实现其方法,如下所示。
- public class Repository<T> : IRepository<T>
- {
- public ValueTask<T> GetData()
- {
- var value = default(T);
- return new ValueTask<T>(value);
- }
- }
下面介绍了如何从 Main 方法调用 GetData 方法。
- static void Main(string[] args)
- {
- IRepository<int> repository = new Repository<int>();
- var result = repository.GetData();
- if(result.IsCompleted)
- Console.WriteLine("Operation complete...");
- else
- Console.WriteLine("Operation incomplete...");
- Console.ReadKey();
- }
现在,让我们向存储库中添加另一个方法,这次是名为 GetDataAsync 的异步方法。以下是修改后的 IRepository 接口的外观。
- public interface IRepository<T>
- {
- ValueTask<T> GetData();
- ValueTask<T> GetDataAsync();
- }
GetDataAsync 方法由 Repository 类实现,如下面给出的代码片段所示。
- public class Repository<T> : IRepository<T>
- {
- public ValueTask<T> GetData()
- {
- var value = default(T);
- return new ValueTask<T>(value);
- }
- public async ValueTask<T> GetDataAsync()
- {
- var value = default(T);
- await Task.Delay(100);
- return value;
- }
- }
何时应在 C# 中使用 ValueTask?
尽管 ValueTask 提供了许多好处,但使用 ValueTask 代替 Task 需要一些权衡。ValueTask 是具有两个字段的值类型,而 Task 是具有单个字段的引用类型。因此,使用 ValueTask 意味着处理更多数据,因为方法调用将返回两个数据字段而不是一个字段。此外,如果等待返回 ValueTask 的方法,则该异步方法的状态机也会更大,因为在 Task 的情况下,它必须容纳包含两个字段的结构,而不是单个引用。
此外,如果异步方法的使用者使用 Task.WhenAll 或 Task.WhenAny,则在异步方法中使用 ValueTask<T> 作为返回类型可能会产生高昂的成本。这是因为您需要使用 AsTask 方法将 ValueTask<T> 转换为 Task<T>这将产生一个分配,如果首先使用了缓存的 Task<T>,则可以轻松避免这种分配。
这是经验法则。当您有一段始终是异步的代码时,即当操作不会立即完成时,请使用 Task。当异步操作的结果已经可用或已有缓存的结果时,请利用 ValueTask。无论哪种方式,您都应该在考虑 ValueTask 之前执行必要的性能分析。
【C# Task】 ValueTask/Task<TResult>的更多相关文章
- 【C# Task】理解Task中的ConfigureAwait配置同步上下文
原文:https://devblogs.microsoft.com/dotnet/configureawait-faq/ 作者:Stephen 翻译:xiaoxiaotank 静下心来,你一定会有收获 ...
- 【SRM-06 D】五色战队&&【codeforces 788E】 New task
原题链接:788E - New task Description 游行寺家里人们的发色多种多样,有基佬紫.原谅绿.少女粉.高级黑.相簿白等. 日向彼方:吾令人观其气,气成五彩,此天子气也. 琉璃:我们 ...
- 【ZOJ 3844】Easy Task
题意 每次把序列中最大的数a的一个和最小的数b的一个变成a-b.求最后是否能使序列里的数全部相同,能则输出这个相同的数. 分析 一定是有解的,不断减少最大数的个数,最大数减少为0个时,就是减少了不同数 ...
- 【C# Task】开篇
概览 在学task类之前必须学习线程的知识. 以下是task命名空间的类的结构图 1.2种任务类型: 有返回值task<TResult> .无返回值task. 2.2座任务工厂 TaskF ...
- 【.NET+MQTT】.NET6 环境下实现MQTT通信,以及服务端、客户端的双边消息订阅与发布的代码演示
前言: MQTT广泛应用于工业物联网.智能家居.各类智能制造或各类自动化场景等.MQTT是一个基于客户端-服务器的消息发布/订阅传输协议,在很多受限的环境下,比如说机器与机器通信.机器与物联网通信等. ...
- 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程
反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) 背景介绍: 为了平衡社区成员的贡献和索取,一起帮引入了帮帮币.当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮 ...
- 【30.93%】【codeforces 558E】A Simple Task
time limit per test5 seconds memory limit per test512 megabytes inputstandard input outputstandard o ...
- 【EWM系列】SAP EWM创建warehouse task的函数
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP EWM创建warehouse ...
- 【C# task】TaskContinuationOptions 位枚举
TaskContinuationOptions 根据 TaskContinuationOptions 的不同,出现了三个分支 LongRunning:独立线程,和线程池无关 包含 PreferFair ...
随机推荐
- WebGPU图形编程(2):构建一个单色的三角形<学习引自徐博士教程>
非常兴奋,我坚持了下来,开始更新我的第二篇博客,还是关于WebGPU的,我在学习过程中,对这项技术非常感兴趣,即使它非常抽象,难以理解,因为我看到未来Web3D的发展,WebGPU会成为主流技术,学习 ...
- 阿里巴巴基于应用和变更的交付模式|阿里巴巴DevOps实践指南
编者按:本文源自阿里云云效团队出品的<阿里巴巴DevOps实践指南>,扫描上方二维码或前往:https://developer.aliyun.com/topic/devops,下载完整版电 ...
- vue 快速入门 系列 —— Vue 实例的初始化过程
其他章节请看: vue 快速入门 系列 Vue 实例的初始化过程 书接上文,每次调用 new Vue() 都会执行 Vue.prototype._init() 方法.倘若你看过 jQuery 的源码, ...
- GitHubPages的域名解析信息
github目录下CNAME修改
- Jvm内存回收
一.什么内存会被回收 可达性分析算法 通过一系列的GC ROOT的对象作为超始点,从这些节点开始向下搜索,搜索所走的路径称为"引用链",当一个对象到GC ROOT之间没有任何引用链 ...
- 关于https域名下的页面iframe嵌套http页面的问题
业务场景:在一个https域名下用iframe嵌套一个http域名的页面,会直接报错的,报错信息如下: 这段话的意思是:http域名的页面是通过https域名页面加载的,在一个安全的页面不允许加载一个 ...
- CF Round #669 Div2
A 可以发现不论往怎样一个串往后加上两个 \(0\) 或两个 \(1\) 其奇数位和偶数位上的差值都是相同的.因此我们两位两位考虑这个 \(01\) 串,对于相邻两位相同那么直接留下,否则留下 \(0 ...
- js判断当前浏览设备
前端开发经常遇到需要判断用户的浏览设备,是pc端还是移动端,移动端使用的是什么手机系统?android.ios.ipad.windows phone等等,有时候还需要知道用户浏览页面是在微信中打开还是 ...
- eclipse使用的步骤
eclipse使用的步骤: 第一步: 选择工作目录. 以后在Eclipse上面写的所有代码都是在工作目录上的. 第二步: 在Project Exploer 窗口上创建一个工程,以后我们写代码都是以工程 ...
- 利用ICMP协议的PING命令获取客户端当前网络质量 by徐文棋
无论在windows下,linux也好,unix也好,都可以通过 Ping命令获得当前设备的网络延迟,延迟是客户端到服务端的网络响应时间.通常延迟越低,反应速度越快 这里使用了SimplePing ...