【问题标题】:Most efficient way to convert Task to Task<Unit>?将 Task 转换为 Task<Unit> 的最有效方法?
【发布时间】:2015-12-17 11:46:51
【问题描述】:

我有一个高吞吐量的队列安排,我接收 Func&lt;Task&gt; 并希望将其投影到 Func&lt;Task&lt;System.Reactive.Unit&gt;&gt; 以很好地适应下游 Rx 系统。

由于Unit.Default 是唯一的价值,感觉这应该很容易,但我希望它尽可能高效。我希望以正确的方式通过原始Task 中的所有异常。

我目前的做法是:

public Task<Unit> QueueTaskRx(Func<Task> task)
{
    Func<Task<Unit>> f = async () =>
    {
        await task();
        return Unit.Default;
    };

    return QueueTask(f);
}

但我担心async/await 的开销

也许另一种更有效的方法是:

public Task<Unit> QueueTaskRx(Func<Task> task)
{
    Func<Task<Unit>> f = () => task().ContinueWith(_ =>
    {
        // What other cases do I need to consider here??
        if (_.IsFaulted && _.Exception != null)
            throw _.Exception.InnerException;

        return Unit.Default;
    }, TaskContinuationOptions.ExecuteSynchronously);

    return QueueTask(f);
}

但这感觉不安全,而且更复杂,分支等

谁有更好的办法?

【问题讨论】:

  • 但我担心 async/await 的开销 为什么?您是否测试过这段代码并发现编译器生成的状态机结构是最重要的开销?
  • @YuvalItzchakov 谢谢 - 是的,我已经在一个紧凑的循环中对其进行了测试,发现使用 async/awaitExecuteSynchronously ContinueWith 相比,它慢了大约 46%
  • 或许您可以与我们分享您的测量代码和性能结果。
  • 我的基准测试似乎很差,并且正在草率下结论 - 我发现了一个问题,现在看到了更接近的结果。
  • 我建议使用BenchmarkDotNet,它会为您完成所有繁重的工作,而您只需编写测试。

标签: c# .net parallel-processing task-parallel-library system.reactive


【解决方案1】:

我会使用async/await,同时添加ConfigureAwait(false)

可以使用ContinueWith 进行操作,但这只会为您节省非常少的时间(例如单个位标志检查和单个参考副本)。这会花费很多复杂性:

  • ContinueWith 应始终指定 TaskScheduler
  • 除了例外,您还应该处理取消,因为 Task 会对其进行特殊处理。

对于其他陷阱,请参阅我的 Tour of Task 博客系列(遗憾的是仍然不完整),我尝试在其中列举使用“旧”API 可能导致的所有问题(例如,ContinueWith)。

【讨论】:

  • 请致电ConfigureAwait(false)
【解决方案2】:

我实现了您所需要的here。我使用了另一个扩展名为 Then 的任务扩展,它的灵感来自 Stephen Toub's blog post

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-08
    • 1970-01-01
    • 2016-10-15
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    • 1970-01-01
    • 2015-09-19
    相关资源
    最近更新 更多