【问题标题】:Is there any sense to return ValueTask from method that internally awaits some Tasks从内部等待某些任务的方法返回 ValueTask 是否有任何意义
【发布时间】:2020-01-29 00:18:23
【问题描述】:

我有一个大致如下所示的异步方法:

async Task<int> ParseStream()
{
    var a = await reader.ReadInt32();
    var b = await reader.ReadInt32();
    return a + b;
}

由于数据已经准备好,该方法大部分时间都会同步工作。 因此,用 ValueTask 替换返回类型以减少分配看起来是个好主意。 但这会调用reader.ReadInt32() return Task。

所以问题是:从内部等待某些任务的方法返回 ValueTask 是否有意义?

【问题讨论】:

  • 我假设readerstream 是相关的/相同的?如果是这样,你怎么知道“大多数时候数据已经准备好了”。如果给我一个任意的Stream,我不能假设有多少数据是现成的,所以看起来你可以做出一些你在问题中没有提到的假设?
  • 哦,我没有注意到这个流 - 这对这种情况并不重要。这个 Parse 方法通常在 response 已经在这里时被调用。
  • 这有点像问“如果我调用的方法分配内存对我来说避免内存分配是否有意义”? (打个比方;节省内存不是ValueTask 的主要目标。)是否使用ValueTask 应该取决于您的 场景使用它是否有意义(因为您限制调用者如何等待您的结果),而不是您的实现当前所依赖的。 (比如说)等待一个使用ValueTask 代替ReadInt32 的新实现然后才切换你的方法是没有意义的。
  • 你是问返回ValueTask&lt;int&gt;而不是Task&lt;int&gt;是否有优势?
  • 是的。例如,假设在某些情况下,此调用 reader.ReadInt32() 会同步运行,我会将返回类型替换为 ValueTask。这种方法会在如此乐观的情况下分配吗?

标签: c# async-await valuetask


【解决方案1】:

更改方法的签名以返回 ValueTask 而不是 Task

async ValueTask<int> ParseStreamAsync()
{
    var a = await reader.ReadInt32Async();
    var b = await reader.ReadInt32Async();
    return a + b;
}

...的优点是调用您的方法的调用者将避免对象分配,以防两个对reader.ReadInt32Async 的调用将同步完成。但这可能不是一个很大的优势,因为对reader.ReadInt32Async 的两次调用可能仍会分别分配一个Task 对象,具体取决于此方法的实现方式。理论上可能会缓存一些常见的Task&lt;Int32&gt; 返回值,但实际上可能性不大。如果返回值是Task&lt;bool&gt;,情况会有所不同,缓存仅有的两个可能值会很便宜。在引入ValueTasks 之前,缓存Task&lt;TResult&gt; 对象是减少分配的唯一方法。

因此,通过使用 ValueTask 而不是 Task,您可以合理地期望每次调用时将对象分配从 3 减少到 2,这不是很令人印象深刻,但也不容忽视。

【讨论】:

    【解决方案2】:

    如果您不确定 ValueTask&lt;T&gt; 是否有用,可能是因为它不是。

    Understanding the Whys, Whats, and Whens of ValueTask

    您可以根据需要多次缓存和等待Task&lt;T&gt;。它只有堆分配的缺点。

    【讨论】:

      【解决方案3】:

      是的,因为它是模式的一部分。您所展示的不是阻塞使用异步模式,因此如果您不使用任务作为回报,它必须是阻塞的。

      【讨论】:

        猜你喜欢
        • 2018-07-31
        • 1970-01-01
        • 1970-01-01
        • 2018-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-01
        相关资源
        最近更新 更多