概要

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>的更多相关文章

  1. 【C# Task】理解Task中的ConfigureAwait配置同步上下文

    原文:https://devblogs.microsoft.com/dotnet/configureawait-faq/ 作者:Stephen 翻译:xiaoxiaotank 静下心来,你一定会有收获 ...

  2. 【SRM-06 D】五色战队&&【codeforces 788E】 New task

    原题链接:788E - New task Description 游行寺家里人们的发色多种多样,有基佬紫.原谅绿.少女粉.高级黑.相簿白等. 日向彼方:吾令人观其气,气成五彩,此天子气也. 琉璃:我们 ...

  3. 【ZOJ 3844】Easy Task

    题意 每次把序列中最大的数a的一个和最小的数b的一个变成a-b.求最后是否能使序列里的数全部相同,能则输出这个相同的数. 分析 一定是有解的,不断减少最大数的个数,最大数减少为0个时,就是减少了不同数 ...

  4. 【C# Task】开篇

    概览 在学task类之前必须学习线程的知识. 以下是task命名空间的类的结构图 1.2种任务类型: 有返回值task<TResult> .无返回值task. 2.2座任务工厂 TaskF ...

  5. 【.NET+MQTT】.NET6 环境下实现MQTT通信,以及服务端、客户端的双边消息订阅与发布的代码演示

    前言: MQTT广泛应用于工业物联网.智能家居.各类智能制造或各类自动化场景等.MQTT是一个基于客户端-服务器的消息发布/订阅传输协议,在很多受限的环境下,比如说机器与机器通信.机器与物联网通信等. ...

  6. 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程

    反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑)   背景介绍: 为了平衡社区成员的贡献和索取,一起帮引入了帮帮币.当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮 ...

  7. 【30.93%】【codeforces 558E】A Simple Task

    time limit per test5 seconds memory limit per test512 megabytes inputstandard input outputstandard o ...

  8. 【EWM系列】SAP EWM创建warehouse task的函数

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP EWM创建warehouse ...

  9. 【C# task】TaskContinuationOptions 位枚举

    TaskContinuationOptions 根据 TaskContinuationOptions 的不同,出现了三个分支 LongRunning:独立线程,和线程池无关 包含 PreferFair ...

随机推荐

  1. 刷机错误ERROR:STATUS_BROM_CMD__FAIL

    ERROR:STATUS_BROM_CMD_STARTCMD_FAIL window驱动没有安装好,几乎所有安装包都有问题,很难成功,成功了也很慢.这是因为之前检测到且烧写错误,然后上一次的驱动错误连 ...

  2. spring事务隔离级别、传播机制以及简单配置

    转自 https://blog.csdn.net/zht741322694/article/details/78676964 一.spring支持的事务声明方式1.  编程式事务  当系统需要明确的, ...

  3. 微信小程序入门教程之一:初次上手

    微信是中国使用量最大的手机 App 之一,日活跃用户超过3亿,月活跃用户超过11亿(2019年底统计),市场极大. 2017年,微信正式推出了小程序,允许外部开发者在微信内部运行自己的代码,开展业务. ...

  4. 感恩陪伴 HelloGitHub 定制的红包封面

    距离放假越来越近了,我们更文的频率也越来越低了. 先别打!听我解释... 我真没偷懒,我是去研究今年的「微信红包封面」玩法了. 这不去年,我们制作的 HelloGitHub 专属红包封面,很多粉丝都说 ...

  5. Linux身份鉴别机制原理

    传统的UNIX身份鉴别机制原理 传统的UNIX身份鉴别即口令认证方式,它主要通过识别用户的用户名或者UID号获取在/etc/shadow中存放的对应用户密码密文等信息,然后获取用户输入密码并采用cry ...

  6. python函数关键字实参传参

    #!/usr/bin/python #coding=utf-8 #好好学习,天天向上 def describe_pet(type,name): print(f"i have a {type} ...

  7. Windows下cmd/powershell命令混淆绕过

    前言 在Windows下绕过杀毒软件的主动防御机制的常见思路. Bypass 1.特殊符号.大小写 常用符号: " ^ , ; 可以绕过一些常规的waf 2.环境变量 拿到一台机器时,可以先 ...

  8. Basler相机启动问题xml读取出错

    切记!同一张网卡上多网口分别连多相机的时候,不要用同一个网段!!很容易出错!

  9. Bootstrap 弹出表单

  10. OSChina 文

    转载请注明来源:https://www.cnblogs.com/hookjc/ http://www.oschina.net/p/carbon-forum   [高性能PHP论坛 Carbon For ...