【问题标题】:Will awaiting multiple tasks observe more than the first exception?等待多个任务会比第一个异常观察到更多吗?
【发布时间】:2021-11-09 14:24:08
【问题描述】:

今天我和我的同事讨论了如何正确处理 C# 5.0 async 方法中的异常,我们想知道一次等待多个任务是否也会观察到运行时未解包的异常。

考虑以下代码sn-p:

async Task ExceptionMethodAsync()
{
    await Task.Yield();
    throw new Exception();
}

async Task CallingMethod()
{
    try
    {
        var a = ExceptionMethodAsync();
        var b = ExceptionMethodAsync();

        await Task.WhenAll(a, b);
    }
    catch(Exception ex)
    {
        // Catches the "first" exception thrown (whatever "first" means)

    }
}

现在第二个任务会发生什么?两者都将处于故障状态,但现在观察到或未观察到第二个任务的异常?

【问题讨论】:

  • C#这本书深入讨论了这个问题,你可以在那里找到答案。

标签: c# asynchronous exception-handling async-await


【解决方案1】:

Task.WhenAll 返回一个任务,与所有任务一样,Exception 属性包含一个组合所有异常的AggregateException

当您await 这样的任务时,实际上只会抛出第一个异常。

... 无论是因为子任务出错,还是因为 Task.WhenAllll 之类的组合器,单个任务可能代表多个操作,并且其中多个操作可能出错。在这种情况下,为了不丢失异常信息(这对于事后调试很重要),我们希望能够表示多个异常,因此对于包装器类型,我们选择了 AggregateException。

...鉴于此,再次选择总是抛出第一个或总是抛出一个聚合,对于“等待”,我们选择总是抛出第一个

来自Task Exception Handling in .NET 4.5

您可以选择是使用await task; 处理第一个(在大多数情况下为true)还是使用task.Exception 处理所有(如下面的示例),但在这两种情况下ab 不会引发 UnobservedTaskException

var task = Task.WhenAll(a, b);
try
{
    await task;
}
catch
{
    Trace.WriteLine(string.Join(", ", task.Exception.Flatten().InnerExceptions.Select(e => e.Message)));
}

【讨论】:

  • 我们应该调用task.Exception.Handle(_ => true) 还是使用await 将子异常标记为“已处理”?
  • @jorgebg 我假设Task.WhenAll“处理”所有单个任务的异常。您需要做的就是处理复合任务Task.WhenAll 返回的异常。所以 AFAIK,await Task.WhenAll(...) 就足够了。
  • 当我需要分别处理每个任务的异常并知道它来自哪个任务时,我想我可以迭代我传递给Task.WhenAll 的所有任务并检查它们的Exception,而不是使用组合任务的Exception,对吧?
猜你喜欢
  • 1970-01-01
  • 2011-12-14
  • 2019-08-20
  • 2012-02-29
  • 1970-01-01
  • 2019-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多