繁体   English   中英

价值任务<TResult>和异步状态机

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM