【问题标题】:Throw Exception inside a Task - "await" vs Wait()在任务中抛出异常 - “await” vs Wait()
【发布时间】:2011-11-12 12:13:04
【问题描述】:
static async void Main(string[] args)
{
    Task t = new Task(() => { throw new Exception(); });

    try
    {                
        t.Start();
        t.Wait();                
    }
    catch (AggregateException e)
    {
        // When waiting on the task, an AggregateException is thrown.
    }

    try
    {                
        t.Start();
        await t;
    }
    catch (Exception e)
    {
        // When awating on the task, the exception itself is thrown.  
        // in this case a regular Exception.
    }           
}

在 TPL 中,当在 Task 中抛出异常时,它会被 AggregateException 包裹。
但使用 await 关键字时,情况并非如此。
这种行为的解释是什么?

【问题讨论】:

  • ..这是await的好处之一

标签: c# exception-handling task-parallel-library async-await parallel-extensions


【解决方案1】:

我们的目标是让它看起来/表现得像同步版本。 Jon Skeet 在他的 Eduasync 系列中很好地解释了这一点,特别是这篇文章:

http://codeblog.jonskeet.uk/2011/06/22/eduasync-part-11-more-sophisticated-but-lossy-exception-handling/

【讨论】:

【解决方案2】:

在 TPL 中使用AggregateException 是因为您可以有多个任务处于等待操作中(任务可以附加子任务),因此其中很多都可以抛出异常。在此处查看子任务中的异常部分:

https://msdn.microsoft.com/ru-ru/library/dd997417(v=vs.110).aspx

await 中,您总是只有一项任务。

另见https://msdn.microsoft.com/ru-ru/library/dd997415(v=vs.110).aspx

【解决方案3】:

Stephen Toub 详细解释了为什么 Task.Wait() 和 await 之间的异常类型存在差异:

Task Exception Handling in .NET 4.5

在 .NET 4 中设计 Task.Wait 时,我们选择始终传播 总计的。该决定受到不覆盖的需要的影响 细节,但也由当时任务的主要用例,即 fork/join 并行性,其中可能出现多个异常 很常见。

虽然类似于 Task.Wait 在高级别(即向前进度 直到任务完成才进行),“等待任务”代表了一个非常 不同的主要场景集。而不是被用于 fork/join 并行性,“等待任务”最常见的用法是 获取一段顺序的、同步的代码并将其转换为 顺序的,异步的代码。在代码中的位置 您执行同步操作,将其替换为 由任务表示的异步操作并“等待”它。因此, 虽然您当然可以将 await 用于 fork/join 操作(例如 使用 Task.WhenAll),这不是 80% 的情况。此外,.NET 4.5 看到介绍 System.Runtime.ExceptionServices.ExceptionDispatchInfo,解决了 允许您跨线程编组异常的问题 不会丢失堆栈跟踪和 Watson 存储桶等异常详细信息。 给定一个异常对象,你将它传递给 ExceptionDispatchInfo.Create,它返回一个 ExceptionDispatchInfo 包含对 Exception 对象的引用和 它的细节。当需要抛出异常时, ExceptionDispatchInfo 的 Throw 方法用于恢复内容 异常并抛出它而不丢失原始信息 (当前调用堆栈信息附加到已经 存储在异常中)。

鉴于此,再次选择总是先扔 或者总是抛出一个聚合,对于“等待”,我们选择总是抛出 首先。但是,这并不意味着您无权访问 相同的细节。在所有情况下,Task 的 Exception 属性仍然 返回一个包含所有异常的 AggregateException,所以 你可以抓住任何抛出的并回去咨询 Task.Exception 需要时。是的,这会导致两者之间的差异 在“task.Wait()”和“await”之间切换时的异常行为 任务”,但我们认为这是两害相权取其轻。

【讨论】:

    猜你喜欢
    • 2015-06-27
    • 1970-01-01
    • 2018-12-06
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-09
    相关资源
    最近更新 更多