系列介绍

简介

【五分钟的dotnet】是一个利用您的碎片化时间来学习和丰富.net知识的博文系列。它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net知识等等。

场景

您可以在下班坐地铁的时候,拿出手机逛一逛博客园,利用短短的五分钟完成阅读。

诞生缘由

  • 曾经学过的内容可能过不了多久就忘了,我们需要一些文章来帮我们查漏补缺。
  • 太长篇幅的文章看着滚动条就害怕了,我们可能更期望文字少的文章。
  • .net体系的内容太多了,平时也不知道该学哪些,我们可能需要一点点知识线索。

文章质量

当然,并不意味着它篇幅短就质量差。所谓麻雀虽小五脏俱全,我们会尽可能保证利用最少的文字去详细的阐述内容。

正文

伴随着dotnet core的不断迭代,我们在享受.net性能上的提升之外,还收获了许许多多新出现的API。不知您有没有发现,有这样一个类型在开始逐渐出现在我们的视野中 ———— ValueTask

比如在最新的EF Core中:

public virtual async ValueTask<EntityEntry> AddAsync(
object entity,
CancellationToken cancellationToken = default)

以上代码是EF Core中DBContext的AddAsync签名,我们可以发现它的返回类型为ValueTask,可能就如同您想的一样,既然AddAsync是这样,那异步查找方法返回的类型也是这样。是的,曾经这些由Task来包裹的结果,现在全部交由VauleTask来处理了。

在最新的C# 8的特性中,引入了 异步流 的概念。它在原有的同步迭代器的基础上,扩充了异步的迭代器版本:

IAsyncEnumerableIAsyncEnumerator

如果您还不了解同步的迭代器接口,可以查看本系列的 上一篇文章

而这个异步迭代器接口的方法签名是这样的:

public interface IAsyncEnumerator<out T> : IAsyncDisposable
{
T Current { get; }
ValueTask<bool> MoveNextAsync();
}

OMG,又是ValueTask!!!

那么,ValueTask到底是什么东西呢?它和传统的Task又有什么区别呢?该在什么时候使用它。

不要慌,接下来的五分钟您将Get到它。

开胃菜

在开始之前,我们先来了解一下咱们.NET中对内存中对象的存储格式:堆与栈。

先来看栈和堆的区别:

  • 栈,或多或少负责跟踪正在程序中运行的代码。栈空间比较小,但是读取速度快
  • 堆,或多或少负责跟踪程序对象或数据。堆空间比较大,但是读取速度慢

而在C#里面(其它.NET语言同理哈),咱们都知道有Class 和 Struct这两个类别,这两个类别对应的就是引用类型和值类型。

我们先拿实例化一个类来说,比如我们在执行 var newInstance = new ClassA()的时候,我们就会建立一个A的对象,而这个对象的数据一般来说就是分配在堆上的,而同时会建立一个引用ID,该ID就一般就置放在栈上面。

那么值类型的数据呢?一般来说它是存放在栈上的。当然这句话不全对:

"值类型存储在栈中, 引用类型存储在堆中” 这句话的前半句是有争议的,“变量的值是在它声明的位置存储的,假如一个类中有一个int类型的实例变量,那么在这个类的任何对象中,该变量的值总是和对象中的其他数据在一起,也就是在堆上,只有局部变量(方法内部声明的变量)和方法参数在栈上。而对于C#2以及更高版本,很多局部变量并不完全存放在栈中”引用-《C# in depth》及译本《深入理解C#》.

这也是为什么我们会将结构化的小数据创建为Struct的原因,比如具有(R,G,B)三个属性的结构Color。

栈里面的数据一般来说因为空间小,读取数据库的原因,它的生命周期就比较小,比如一个返回值为int的方法,当方法完成之后,该栈中的数据就销毁了。而堆呢?堆保存了几乎所有类中的数据,它怎么销毁数据来保存内存不溢出呢? 是的,您会想到GC,在.NET中就是一个专门的垃圾回收器来完成该操作。

开始飙车

回到本篇文章的主题,ValueTask。 Task可能大家都用的比较多了,毕竟从DotNET Framework的年代就流传至今,而ValueTask却从DotNET Core2.0才引入。

我们先来看看 MSDN 中对ValueTask的阐述:

提供异步操作的可等待结果。提供包装 Task 和 TResult(仅使用其中之一)的值类型。

往下滑MSDN,就能看到里面有一个很重要的一点:

There are tradeoffs to using a ValueTask instead of a Task. For example, while a ValueTask can help avoid an allocation in the case where the successful result is available synchronously, it also contains multiple fields, whereas a Task as a reference type is a single field.

不要问为什么这个是英文,因为我尝试MSDN的机翻。唉…………能读懂个鬼,强烈建议给MSDN负责翻译的人员扣鸡腿。

上面大致的意思就是说,ValueTask会避免同步情况下一些不必要的内存分配,从而提升应用整体的性能。

所以说,现在就能明白ValueTask出现的目的是为了提升性能,而被提升的对象就是Task。二位秋名山车神的竞速之路:

如果您足够仔细,您会发现我上面说的是同步的情况。 “???纳尼,我用Task不是异步吗?怎么成同步了?”

别急,回想下您是否写过这样的代码:

return Task.FromResult(42);

您肯定写过(就算没写过也看过

【5min+】 秋名山的竞速。 ValueTask 和 Task的更多相关文章

  1. 【C# Task】 ValueTask/Task<TResult>

    概要 1.如果异步方法的使用者使用 Task.WhenAll 或 Task.WhenAny,则在异步方法中使用 ValueTask<T> 作为返回类型可能会产生高昂的成本.这是因为您需要使 ...

  2. 深入理解 ValueTask

    深入理解 ValueTask .NET Framework 4 里面的命名空间为 System.Threading.Tasks的 Task 类.这个类以及它派生的 Task<TResult> ...

  3. 【5min+】你怎么穿着品如的衣服?IEnumerable AND IEnumerator

    系列介绍 简介 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的. ...

  4. 理解C#中的ValueTask

    原文:https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/ 作者:Ste ...

  5. 如何使用 C# 中的 ValueTask

    在 C# 中利用 ValueTask 避免从异步方法返回 Task 对象时分配 翻译自 Joydip Kanjilal 2020年7月6日 的文章 <How to use ValueTask i ...

  6. 理解Task和和async await

    本文将详解C#类当中的Task,以及异步函数async await和Task的关系 一.Task的前世今生 1.Thread 一开始我们需要创建线程的时候一般是通过Thread创建线程,一般常用创建线 ...

  7. C#:终于有人把 ValueTask、IValueTaskSource、ManualResetValueTaskSourceCore 说清楚了!

    目录 1,可用版本与参考资料 2,ValueTask 和 Task 3,编译器如何编译 4,ValueTask 有什么优势 5,ValueTask 创建异步任务 6,IValueTaskSource ...

  8. HttpClient Received an unexpected EOF or 0 bytes from the transport stream

    请求https链接时报错,奇怪的是pc1正常,pc2异常 Unhandled Exception: System.AggregateException: One or more errors occu ...

  9. async/await使用深入详解

    async和await作为异步模型代码编写的语法糖已经提供了一段时间不过一直没怎么用,由于最近需要在BeetleX webapi中集成对Task方法的支持,所以对async和await有了深入的了解和 ...

随机推荐

  1. js中的数据类型及常用属性和方法

    JavaScript 字符串 字符串(或文本字符串)是一串字符(比如 "Bill Gates").字符串被引号包围.您可使用单引号或双引号您可以在字符串内使用引号,只要这些引号与包 ...

  2. 自动为DEV GridView控件添加SizeChanged事件

    实现gdv设置的抽象对象,不用每个gdv控件都添加sizechanged事件,只需执行gdc绑定sql函数,在其中会自动添加SizeChanged事件. //2016.5.13 by sngk //根 ...

  3. Python--day24--单继承关键字super

    super().  调用父类方法:(super不仅可以在一个类的内部使用,还可以在一个类的外部使用)

  4. 【9101】求n!的值

    Time Limit: 10 second Memory Limit: 2 MB 问题描述 用高精度的方法,求n!的精确值(n的值以一般整数输入). Input 文件输入仅一行,输入n. Output ...

  5. 2018-8-10-win10-uwp-绑定-OneWay-无法使用

    title author date CreateTime categories win10 uwp 绑定 OneWay 无法使用 lindexi 2018-08-10 19:16:50 +0800 2 ...

  6. P1035 台阶问题二

    题目描述 有 \(N\) 级的台阶,你一开始在底部,每次可以向上迈最多 \(K\) 级台阶(最少 \(1\) 级),问到达第 \(N\) 级台阶有多少种不同方式. 输入格式 两个正整数 \(N, K( ...

  7. P1030 队列的基本操作

    题目描述 现在给你一个队列,它一开始是空的,你需要模拟队列的操作.队列的操作包括如下: "push x":将元素 x 放入队列中,其中x是一个int范围内的整数: "po ...

  8. slot的使用方法

    参考链接:https://www.cnblogs.com/loveyt/p/9946450.html 插槽的使用其实是很简单,你只需明白以下两点,就很容易理解. 1.插槽是使用在子组件中的, 2.插槽 ...

  9. Linux 内核kobject 层次, kset, 和子系统

    kobject 结构常常用来连接对象到一个层级的结构中, 匹配正被建模的子系统的结构. 有 2 个分开的机制对于这个连接: parent 指针和 ksets. 在结构 kobject 中的 paren ...

  10. dotnet 使用 lz4net 压缩 Stream 或文件

    在 dotnet 可以使用 LZ4 这个无损的压缩算法,这个压缩算法的压缩率不高但是速度很快.这个库支持在 .NET Standard 1.6 .NET Core .NET Framework Mon ...