概要

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. 雷柏鼠标vt350Q配对

    vt350q 闲鱼捡了个垃圾vt350q,23元,无接收器,不知道好坏 鼠标线 拿到手插上线没法用,后来用了罗技anywhere2s的线可以,原来usb鼠标线是五根. 鼠标毛病 使用后发现滚轮有时候乱 ...

  2. 2021年SpringBoot面试题200道及答案

    https://blog.csdn.net/yanpenglei/article/details/120822218 https://blog.csdn.net/ldb987/article/deta ...

  3. gin源码解读2-揭开gin的神秘面纱

    数据如何在gin中流转 func main() { gin.SetMode(gin.DebugMode) // 设置为开发模式 router := gin.Default() _ = router.S ...

  4. linux文件权限全面解析

    目录 linux文件权限全面解析 一:linux文件的权限有哪些? 1,权限分为3个部分 2,权限位 3,每一个权限拥有一个数字编号 4,在添加权限的时候,可以将权限加起来 5,linux添加权限命令 ...

  5. HashMap相关

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] ta ...

  6. Azure AD Domain Service(一)将 Azure VM 实例添加到域服务里

    一,引言 有网友提到实际项目中如何将 Azuer VM 实例加入到 Azure AD 域,那我们今天就带着整个问题开始今天的分析!首先我们得了解什么是 Azure AD 域服务,Azure AD 域服 ...

  7. C++读写图片文件

    1.C方式 string sourcefilename = "D:\\Logo.jpg"; string destfilename="D:\\Logo1.jpg" ...

  8. Matplotlib 绘图秘籍·翻译完成

    原文:Matplotlib Plotting Cookbook 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. 在线阅读 ApacheCN ...

  9. [POI2010]TEL-Teleportation

    因为题目中要求 \(1 \sim 2\) 的最短路只有 \(5\),于是我们可以考虑直接使用人脑将图分层. 那么我们怎么定义每层的点呢?因为要使 \(1 \sim 2\) 的最短路只有 \(5\),我 ...

  10. hihoCoder挑战赛1 毁灭者问题

    题目链接:http://hihocoder.com/problemset/problem/1034 数据结构题,由于每个魔法单位有着不同的回复速度和上限,所以不能根据吸收时间点进行查询和更新.但是如果 ...