将Task转换为Task<;单位>;

本文关键字:Task 单位 gt lt 转换 | 更新日期: 2023-09-27 18:27:14

我有一个高通量排队安排,通过它我接收一个Func<Task>,并希望将其投影到Func<Task<System.Reactive.Unit>>,以很好地适应下游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);
}

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

有人有更好的方法吗?

将Task转换为Task<;单位>;

我将使用async/await,同时添加一个ConfigureAwait(false)

可以使用ContinueWith,但这只会为您节省一些非常小的时间(比如单个位标志检查和单个引用副本)。这将花费大量的复杂性:

  • ContinueWith应始终指定一个TaskScheduler
  • 除了例外情况外,您还应该处理取消,因为Task对此进行了特殊处理

关于其他陷阱,请参阅我的Task之旅博客系列(遗憾的是,该系列仍然不完整),我试图列举使用"旧"API可能导致的所有问题(例如,ContinueWith)。

我在这里实现了您所需要的。我使用了另一个名为Then的任务扩展,它的灵感来自Stephen Toub的博客文章。