为什么在 C# 中使用 Task<T> 而不是 ValueTask<T>?
Why would one use Task<T> over ValueTask<T> in C#?
自 C# 7.0 起,异步方法可以 return ValueTask。解释说当我们有缓存结果或通过同步代码模拟异步时应该使用它。但是,我仍然不明白始终使用 ValueTask 有什么问题,或者实际上为什么 async/await 不是从一开始就使用值类型构建的。 ValueTask 什么时候无法完成这项工作?
来自 the API docs(强调已添加):
Methods may return an instance of this value type when it's likely that the result of their operations will be available synchronously and when the method is expected to be invoked so frequently that the cost of allocating a new Task<TResult>
for each call will be prohibitive.
There are tradeoffs to using a ValueTask<TResult>
instead of a Task<TResult>
. For example, while a ValueTask<TResult>
can help avoid an allocation in the case where the successful result is available synchronously, it also contains two fields whereas a Task<TResult>
as a reference type is a single field. This means that a method call ends up returning two fields worth of data instead of one, which is more data to copy. It also means that if a method that returns one of these is awaited within an async
method, the state machine for that async
method will be larger due to needing to store the struct that's two fields instead of a single reference.
Further, for uses other than consuming the result of an asynchronous operation via await
, ValueTask<TResult>
can lead to a more convoluted programming model, which can in turn actually lead to more allocations. For example, consider a method that could return either a Task<TResult>
with a cached task as a common result or a ValueTask<TResult>
. If the consumer of the result wants to use it as a Task<TResult>
, such as to use with in methods like Task.WhenAll
and Task.WhenAny
, the ValueTask<TResult>
would first need to be converted into a Task<TResult>
using AsTask
, which leads to an allocation that would have been avoided if a cached Task<TResult>
had been used in the first place.
As such, the default choice for any asynchronous method should be to return a Task
or Task<TResult>
. Only if performance analysis proves it worthwhile should a ValueTask<TResult>
be used instead of Task<TResult>
.
However I still do not understand what is the problem with using ValueTask always
结构类型不是免费的。复制大于引用大小的结构可能比复制引用慢。存储大于引用的结构比存储引用占用更多内存。当可以注册引用时,可能不会注册大于 64 位的结构。较低收集压力的好处可能不会超过成本。
性能问题应该通过工程学科来解决。制定目标,根据目标衡量您的进度,然后决定如果未达到目标如何修改程序,并在整个过程中进行衡量以确保您的更改实际上是改进。
why async/await wasn't built with a value type from the start.
在 Task<T>
类型已经存在很久之后,await
被添加到 C# 中。在已有类型的情况下发明一种新类型有点不合常理。 await
在确定 2012 年交付的产品之前经历了多次设计迭代。完美是优秀的敌人;最好发布一个与现有基础设施配合良好的解决方案,然后如果有用户需求,稍后提供改进。
我还注意到,允许用户提供的类型作为编译器生成的方法的输出的新功能增加了相当大的风险和测试负担。当你唯一能 return 的东西是 void 或任务时,测试团队不必考虑任何 returned 某些绝对疯狂类型的场景。测试编译器意味着不仅要弄清楚人们可能会编写什么程序,还要弄清楚什么程序是 可能 编写的,因为我们希望编译器编译所有合法程序,而不仅仅是所有合理的程序.好贵啊
Can someone explain when ValueTask would fail to do the job?
这件事的目的是提高性能。如果它不能可衡量地 和显着 提高性能,它就无法完成工作。不能保证一定会。
有一些changes in .Net Core 2.1。从 .net core 2.1 开始,ValueTask 不仅可以表示同步完成的动作,还可以表示异步完成的动作。此外,我们收到 non-generic ValueTask
类型。
我将留下与您的问题相关的 Stephen Toub comment:
We still need to formalize guidance, but I expect it'll be something
like this for public API surface area:
Task provides the most usability.
ValueTask provides the most options for performance optimization.
If you're writing an interface / virtual method that others will override, ValueTask is the right default choice.
If you expect the API to be used on hot paths where allocations will matter, ValueTask is a good choice.
Otherwise, where performance isn't critical, default to Task, as it provides better
guarantees and usability.
From an implementation perspective, many of
the returned ValueTask instances will still be backed by Task.
功能不仅可以在.net core 2.1中使用。您将能够将它与 System.Threading.Tasks.Extensions 包一起使用。
来自 Marc 的最新信息(2019 年 8 月)
Use Task when something is usually or always going to be genuinely asynchronous, i.e. not immediately complete; use ValueTask when something is usually or always going to be synchronous, i.e. the value will be known inline; also use ValueTask in a polymorphic scenario (virtual, interface) where you can't know the answer.
来源:https://blog.marcgravell.com/2019/08/prefer-valuetask-to-task-always-and.html
当我有类似的问题时,我关注了上面的博客 post 最近的一个项目。
自 C# 7.0 起,异步方法可以 return ValueTask
来自 the API docs(强调已添加):
Methods may return an instance of this value type when it's likely that the result of their operations will be available synchronously and when the method is expected to be invoked so frequently that the cost of allocating a new
Task<TResult>
for each call will be prohibitive.There are tradeoffs to using a
ValueTask<TResult>
instead of aTask<TResult>
. For example, while aValueTask<TResult>
can help avoid an allocation in the case where the successful result is available synchronously, it also contains two fields whereas aTask<TResult>
as a reference type is a single field. This means that a method call ends up returning two fields worth of data instead of one, which is more data to copy. It also means that if a method that returns one of these is awaited within anasync
method, the state machine for thatasync
method will be larger due to needing to store the struct that's two fields instead of a single reference.Further, for uses other than consuming the result of an asynchronous operation via
await
,ValueTask<TResult>
can lead to a more convoluted programming model, which can in turn actually lead to more allocations. For example, consider a method that could return either aTask<TResult>
with a cached task as a common result or aValueTask<TResult>
. If the consumer of the result wants to use it as aTask<TResult>
, such as to use with in methods likeTask.WhenAll
andTask.WhenAny
, theValueTask<TResult>
would first need to be converted into aTask<TResult>
usingAsTask
, which leads to an allocation that would have been avoided if a cachedTask<TResult>
had been used in the first place.As such, the default choice for any asynchronous method should be to return a
Task
orTask<TResult>
. Only if performance analysis proves it worthwhile should aValueTask<TResult>
be used instead ofTask<TResult>
.
However I still do not understand what is the problem with using ValueTask always
结构类型不是免费的。复制大于引用大小的结构可能比复制引用慢。存储大于引用的结构比存储引用占用更多内存。当可以注册引用时,可能不会注册大于 64 位的结构。较低收集压力的好处可能不会超过成本。
性能问题应该通过工程学科来解决。制定目标,根据目标衡量您的进度,然后决定如果未达到目标如何修改程序,并在整个过程中进行衡量以确保您的更改实际上是改进。
在why async/await wasn't built with a value type from the start.
Task<T>
类型已经存在很久之后,await
被添加到 C# 中。在已有类型的情况下发明一种新类型有点不合常理。 await
在确定 2012 年交付的产品之前经历了多次设计迭代。完美是优秀的敌人;最好发布一个与现有基础设施配合良好的解决方案,然后如果有用户需求,稍后提供改进。
我还注意到,允许用户提供的类型作为编译器生成的方法的输出的新功能增加了相当大的风险和测试负担。当你唯一能 return 的东西是 void 或任务时,测试团队不必考虑任何 returned 某些绝对疯狂类型的场景。测试编译器意味着不仅要弄清楚人们可能会编写什么程序,还要弄清楚什么程序是 可能 编写的,因为我们希望编译器编译所有合法程序,而不仅仅是所有合理的程序.好贵啊
Can someone explain when ValueTask would fail to do the job?
这件事的目的是提高性能。如果它不能可衡量地 和显着 提高性能,它就无法完成工作。不能保证一定会。
有一些changes in .Net Core 2.1。从 .net core 2.1 开始,ValueTask 不仅可以表示同步完成的动作,还可以表示异步完成的动作。此外,我们收到 non-generic ValueTask
类型。
我将留下与您的问题相关的 Stephen Toub comment:
We still need to formalize guidance, but I expect it'll be something like this for public API surface area:
Task provides the most usability.
ValueTask provides the most options for performance optimization.
If you're writing an interface / virtual method that others will override, ValueTask is the right default choice.
If you expect the API to be used on hot paths where allocations will matter, ValueTask is a good choice.
Otherwise, where performance isn't critical, default to Task, as it provides better guarantees and usability.
From an implementation perspective, many of the returned ValueTask instances will still be backed by Task.
功能不仅可以在.net core 2.1中使用。您将能够将它与 System.Threading.Tasks.Extensions 包一起使用。
来自 Marc 的最新信息(2019 年 8 月)
Use Task when something is usually or always going to be genuinely asynchronous, i.e. not immediately complete; use ValueTask when something is usually or always going to be synchronous, i.e. the value will be known inline; also use ValueTask in a polymorphic scenario (virtual, interface) where you can't know the answer.
来源:https://blog.marcgravell.com/2019/08/prefer-valuetask-to-task-always-and.html
当我有类似的问题时,我关注了上面的博客 post 最近的一个项目。