我读了一篇关于 ValueTask 的文章——出现了几个问题,
这里是文章中的代码:
int bytesRead;
{
ValueTask<int> readTask = _connection.ReadAsync(buffer);
if (readTask.IsCompletedSuccessfully)
{
bytesRead = readTask.Result;
}
else
{
using (_connection.RegisterCancellation())
{
bytesRead = await readTask;
}
}
}
它手动询问任务是否已结束,如果已结束,则同步执行所有操作;没有结束 - 异步。
还有一些我不明白的东西:
- 这是什么奇怪的方法
_connection.RegisterCancellation()
- 并且不能编译器本身而不是一个词
await
分支到同步和异步的情况下。也许是新版本?也许他们没有故意把它放在编译器中?
你的问题可以分为两部分
Task
相对ValueTask
我必须马上说,从技术上讲,问题的这两个部分没有任何联系,但在逻辑上是有联系的。
代码是同步还是异步不是由编译器决定的,而是由你决定的。要将异步状态机注入到方法中,您需要显式指定
async
. 要向这台机器添加状态,请添加await
.如果方法在方法执行路径上遇到,则如果从被调用方法接收到不完整的/
await
,则该方法将异步执行。否则,该方法将像常规同步方法一样同步执行。await
Task
ValueTask
此类分支的最简单示例是找出您的 IP:
如果此代码中的所有内容都
Task
更改为ValueTask
(exceptMain
) - 什么都不会改变。控制台输出
在这里,第一个方法调用异步完成,其余的同步完成。
不同之处在于它
ValueTask
比 更便宜,更容易创建Task
,但它有其局限性。例如,不能在多个地方同时进行。它应该在您链接的文章中。我没有读过它,但我读过它的翻译原文。总而言之,你不应该太担心编译器想出什么,但你应该区分同步和异步方法执行。
是否使用
ValueTask
?除非您的代码对性能非常敏感,否则不要使用。例如,如果您正在编写客户端应用程序代码,则根本不必考虑它。但是,如果您正在为将同时为许多用户提供服务的服务器编写异步代码,那么ValueTask
作为节省资源的一种方式,这将派上用场。这种节省只有在高负载的情况下才会很明显。为了回答这种节省来自哪里,您需要了解值类型与引用类型在性能方面有何不同,您需要了解何时使用类更好,何时使用结构、
Task
类、ValueTask
结构. 然后就会清楚为什么要ValueTask
发明整个故事。换句话说,使用它更安全,更容易
Task
,默认使用它。如果您意识到为每个方法调用创建一个类实例对您来说既慢又昂贵,请实现它ValueTask
,但这些都是优化的精妙之处。