【问题标题】:Is there a simple way to return a task with an exception?有没有一种简单的方法可以返回异常的任务?
【发布时间】:2015-06-03 20:10:19
【问题描述】:

我的理解是return Task.FromResult(foo)是一个简单的简写:

var tcs = new TaskCompletionSource<TFoo>();
tcs.SetResult(foo);
return tcs.Task;

对于返回异常状态的任务是否有一些等价物?

var tcs = new TaskCompletionSource<TFoo>();
tcs.SetException(new NotSupportedException()); // or whatever is appropriate
return tcs.Task;

我没有看到像 Task.FromException 这样的东西。还是直接抛出异常而不返回任务更合适?

【问题讨论】:

  • 看着你的代码让我想选择它并在 ReSharper 中使用“提取方法”。
  • 看来这种方法是故意不暴露的:它存在,但是是internal
  • 这种方法的问题是您返回的异常不会有堆栈跟踪,因为它实际上从未被抛出。也许这就是 FromException 没有被暴露的原因。
  • @JohnSaunders - 当然,我可以把它放在一个方法中,但我更感兴趣的是这是不是正确的方法。
  • @ThomasLevesque - 嗯......所以我应该扔掉它,抓住它然后在任务中返回它吗?我不认为它应该被扔掉。还是应该?

标签: c# task-parallel-library async-await


【解决方案1】:

从 .NET 4.6 开始,BCL 中有一个 Task.FromException 方法。

还有Task.FromCanceled

【讨论】:

    【解决方案2】:

    我的理解是 return Task.FromResult(foo) 很简单 ... [TaskCompletionSource.SetResult] 的简写。

    其实Task.FromResult不用TaskCompletionSourceits implementation比那简单很多。

    对于返回异常状态的任务是否有一些等价物?

    我认为TaskCompletionSource 将是最好的选择。你也可以这样做:

    static Task FromExAsync(Exception ex) 
    {
        var task = new Task(() => { throw ex; });
        task.RunSynchronously();
        return task;
    }
    

    异常不会传播到返回的task 之外,直到通过await tasktask.Wait() 观察到,这应该是一种期望的行为。

    请注意,如果传递给 FromExAsync 的异常是一个活动异常(即已在其他地方抛出并捕获),则像这样重新抛出它会丢失当前堆栈跟踪和存储在异常中的 Watson 存储桶信息。有两种处理方式:

    • AggregateException 包装异常。这将使原始异常以AggregateException.InnerException 的形式提供:

    static Task FromExAsync(Exception ex) 
    {
        var task = new Task(() => { throw new AggregateException(ex); });
        task.RunSynchronously();
        return task;
    }
    
    • 使用ExceptionDispatchInfo.Capture 传递活动异常的状态:

    static Task FromExAsync(Exception ex) 
    {
        var ei = System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex);
        var task = new Task(() => { ei.Throw(); });
        task.RunSynchronously();
        return task;
    }
    

    最后,也许最简单但最糟糕的选择(由于overhead of the state machine 和编译器警告)是从async 方法中抛出:

    static async Task FromExAsync(Exception ex)
    {
        throw ex;
    }
    

    【讨论】:

    • 考虑到有关开销的讨论,也许可以考虑使用自定义等待器?
    • @Aron,我看不出自定义等待者如何消除 async 和任务机器,因为 @MattJohnson 需要一个 Task 对象作为返回值。更多详情,请查看this
    • 谢谢。异步编程非常强大,但有时即使是最小的事情也会让人感到困惑。
    • @hvd,.NET 4.6 中有新的Task.FromException API:blogs.msdn.com/b/pfxteam/archive/2015/02/02/…
    • @Noseratio 嘿,很好,谢谢。我在看 .NET 4.5,它已经有 FromException,只是还没有公开。
    猜你喜欢
    • 1970-01-01
    • 2011-04-14
    • 2011-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-21
    • 2011-11-03
    • 2010-10-25
    相关资源
    最近更新 更多