[英]The ValueTask<TResult> and the async state machine
根据文档, ValueTask<TResult>
...
提供包装
Task<TResult>
和TResult
的值类型,仅使用其中一个。
我的问题是关于 C# 编译器在遇到async
关键字时生成的状态机。 是不是足够聪明,以产生ValueTask<TResult>
一个包装了TResult
,当结果是立即可用,或者一个一个包装了Task<TResult>
当结果来了之后await
? 下面是一个例子:
static async ValueTask<DateTime> GetNowAsync(bool withDelay)
{
if (withDelay) await Task.Delay(1000);
return DateTime.Now;
}
static void Test()
{
var t1 = GetNowAsync(false);
var t2 = GetNowAsync(true);
}
调用GetNowAsync(false)
应该返回一个TResult
包装器,因为没有等待,调用GetNowAsync(true)
应该返回一个Task<TResult>
包装器,因为在结果可用之前等待Task.Delay
。 我担心状态机总是返回Task
包装器的可能性,使ValueTask
类型相对于Task
所有优点无效(并保留所有缺点)。 据我所知, ValueTask<TResult>
类型的属性没有提供关于它在内部包装什么的指示。 我将上面的代码粘贴到了sharplab.io ,但输出也没有帮助我回答这个问题。
我想我应该回答我自己的问题,因为我现在知道答案了。 答案是我的担心是没有根据的: C# 编译器足够聪明,可以在每种情况下发出正确类型的ValueTask<TResult>
。 当结果同步可用时,它会发出一个值包装器,当结果不可用时,它会发出一个任务包装器。
我通过性能测量得出了这个结论:通过测量每种情况下分配的内存,以及创建相同数量的任务所需的时间。 结果清晰且一致。 例如, ValueTask<int>
在包装int
值时正好消耗 12 个字节,而在包装Task<int>
时正好消耗 48 个字节,因此毫无疑问在幕后发生了什么。
编译器很笨,可以按照它所说的去做:
https://source.dot.net/#System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs,409
[AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
[StructLayout(LayoutKind.Auto)]
public readonly struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
小心使用ValueTask
:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.