繁体   English   中英

转换 ValueTask<t> 到一个非通用的 ValueTask</t>

[英]Convert a ValueTask<T> to a non generic ValueTask

这里提出的问题与此处的问题相同,旨在为其创建一个明确的解决方案。 最准确的答案是 Stephen Toub 本人在本期中正是关于这个问题的。 “推荐代码”如下:

public static ValueTask AsValueTask<T>(this ValueTask<T> valueTask)
{
    if (valueTask.IsCompletedSuccessfully)
    {
        valueTask.GetResult();
        return default;
    }

    return new ValueTask(valueTask.AsTask());
}

这个答案不是最新的 - ValueTask 不公开 GetResult() (只有一个 Result 属性) - 问题是:

  • 我们是否需要将Result从ValueTask中“拉”出来(以“释放”可能在这个ValueTask下运行的IValueTaskSource)?
  • 如是:
    • 是上面缺少的.GetAwaiter()调用吗?
    • 或者是对该属性的虚假调用保证有效var fake = valueTask.Result; 总是? (我害怕死代码消除。)
  • 如果不是,像下面这样的直接实现是否足够(并且是最佳的)?
public static ValueTask AsNonGenericValueTask<T>( in this ValueTask<T> valueTask )
{
    return valueTask.IsCompletedSuccessfully ? default : new ValueTask( valueTask.AsTask() );
}

该代码中缺少的是.GetAwaiter()

public static ValueTask AsValueTask<T>(this ValueTask<T> valueTask)
{
    if (valueTask.IsCompletedSuccessfully)
    {
        valueTask.GetAwaiter().GetResult();
        return default;
    }

    return new ValueTask(valueTask.AsTask());
}

你部分正确,因为你不关心结果。 但是您可能会关心如果您不查询结果会错过的抛出异常或取消。

或者你可以这样写:

public static async ValueTask AsValueTask<T>(this ValueTask<T> valueTask)
    => await valueTask;

您可以使用:

valueTask.GetAwaiter().GetResult();

...或者:

_ = valueTask.Result;

这两个都委托给基础IValueTaskSource<T>.GetResult方法,假设ValueTask<T>IValueTaskSource<T>支持。 使用更短的(第二种)方法应该稍微更有效,因为它涉及更少的方法调用。

您也可以完全忽略获取结果。 没有要求必须至少等待ValueTask<T>一次,或者必须至少检索一次其结果。 交给ValueTask<T>然后忘记它是完全有效的。 记录的限制是:

ValueTask<TResult>实例只能等待一次,[...]

这是“只能” ,而不是“必须”

尽管如此,获得结果仍然是一个好主意。 通过检索结果,您表示ValueTask<TResult>已被使用,因此可以重用底层的IValueTaskSource<T> 重用IValueTaskSource<T>实例使基于ValueTask<T>的实现更加高效,因为它们分配 memory 的频率较低。 要获得一个想法,请查看内部System.Threading.Channels.AsyncOperation<TResult> class。此 class 实现了IValueTaskSource<TResult>接口。 以下是GetResult的实现方式:

/// <summary>Gets the result of the operation.</summary>
/// <param name="token">The token that must match <see cref="_currentId"/>.</param>
public TResult GetResult(short token)
{
    if (_currentId != token)
    {
        ThrowIncorrectCurrentIdException();
    }

    if (!IsCompleted)
    {
        ThrowIncompleteOperationException();
    }

    ExceptionDispatchInfo? error = _error;
    TResult? result = _result;
    _currentId++;

    if (_pooled)
    {
        Volatile.Write(ref _continuation, s_availableSentinel); // only after fetching all needed data
    }

    error?.Throw();
    return result!;
}

将私有字段Action<object> _continuation设置为 static 只读值s_availableSentinel允许AsyncOperation<TResult>被后续异步操作重用(例如ChannelReader.ReadAsync )。 否则下一个异步操作将分配一个新的AsyncOperation<TResult>实例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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