1

我有一个高吞吐量排队安排,我接受Func<Task>并希望将其投影到Func<Task<System.Reactive.Unit>>以很好地适应下游Rx系统。将任务转换为任务<Unit>的最有效方法?

由于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); 
} 

但是这并未”感觉安全和更复杂,分支等

有没有人有更好的方法?

+2

*但我担心async/await的开销*为什么?你测试过这段代码,发现编译器生成的状态机结构是最重要的开销吗? –

+0

@YuvalItzchakov谢谢 - 是的,我已经在一个紧密的循环中进行了测试,发现使用'async/await'和'ExecuteSynchronously'' ContinueWith'的速度慢了大约46%。 – jamespconnor

+2

也许您可以与我们分享您的测量代码和性能结果。 –

回答

3

我会使用async/await,也增加了一个ConfigureAwait(false)

可以ContinueWith做到这一点,但是这将不仅节省您的时间真有些微小的量(像一个单一的逐位标志检查和单一的参考副本)。这将花费大量的复杂性:

  • ContinueWith应始终指定一个TaskScheduler
  • 除了例外,你应该处理取消,因为这是由Task特别处理。

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

+0

调用'ConfigureAwait(false)' – jamespconnor

相关问题