我想等待抛出AggregateException,而不仅仅是第一个Exception

等待故障任务(具有exception集的任务)时, await将重新抛出存储的exception。 如果存储的exception是AggregateException ,它将重新抛出第一个并丢弃其余的exception。

我们如何使用await并同时抛出原始的AggregateException以便我们不会意外丢失错误信息?

注意,当然可以考虑使用hacky解决方案(例如,围绕await尝试catch,然后调用Task.Wait )。 我真的希望找到一个干净的解决方案。 这里最好的做法是什么?

我想过使用自定义awaiter,但内置的TaskAwaiter包含了许多魔法,我不知道如何完全重现。 它调用TPL类型的内部API。 我也不想重现所有这些。

如果你想玩它,这是一个简短的repro:

 static void Main() { Run().Wait(); } static async Task Run() { Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") }; await Task.WhenAll(tasks); } static Task CreateTask(string message) { return Task.Factory.StartNew(() => { throw new Exception(message); }); } 

Run只抛出两个exception中的一个。

请注意,Stack Overflow上的其他问题无法解决此特定问题。 建议重复时请小心。

我不同意你的问题标题中暗示await的行为是不受欢迎的。 在绝大多数场景中都有意义。 在WhenAll情况下,您是否经常需要知道所有错误详细信息,而不仅仅是一个?

AggregateException的主要困难是exception处理,即您失去了捕获特定类型的能力。

也就是说,使用扩展方法很容易获得所需的行为:

 public static async Task WithAggregateException(this Task source) { try { await source.ConfigureAwait(false); } catch { throw source.Exception; } } 

exception处理(任务并行库)

我可以说更多,但它只是填充。 玩它,它确实像他们说的那样工作。 你必须要小心。

也许你想要这个

上帝(Jon Skeet)解释等待exception处理

(我个人躲避等待,但这只是我的偏好)

回应评论(评论回复太长)

然后使用线程作为类似参数的起点,因为最佳实践将是这里的源的源。

除非您实现代码以将其传递出去(例如,await可能包装的异步模式…您在引发事件时将它们添加到事件args对象中),例外情况很快就会被吞噬。 如果您有一个场景,您可以在其中启动任意数量的线程并对其执行,则无法控制订单或终止每个线程的点。 此外,如果一个错误与另一个错误相关,您将永远不会使用此模式。 因此,你强烈暗示其余的执行是完全独立的 – IE你强烈暗示这些线程上的exception已经作为exception处理。 如果你想在它们出现的线程(这是bizzarre)中处理这些线程中的exception以外的事情,你应该将它们添加到通过引用传入的锁定集合中 – 你不再将exception视为exception而是作为一块信息 – 使用并发包,将exception包装在您需要的信息中以识别它来自的上下文 – 这将被传递到它中。

不要混淆您的用例。

我知道我迟到了,但我发现这个整洁的小技巧可以做你想要的。 由于等待任务可以使用完整的exception集,因此调用此Task的Wait或.Result会抛出一个聚合exception。

  static void Main(string[] args) { var task = Run(); task.Wait(); } public static async Task Run() { Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") }; var compositeTask = Task.WhenAll(tasks); try { await compositeTask.ContinueWith((antecedant) => { }, TaskContinuationOptions.ExecuteSynchronously); compositeTask.Wait(); } catch (AggregateException aex) { foreach (var ex in aex.InnerExceptions) { Console.WriteLine(ex.Message); } } } static Task CreateTask(string message) { return Task.Factory.StartNew(() => { throw new Exception(message); }); } 

我不想放弃这种做法,只能抓住我期望的例外情况。 这引出了以下扩展方法:

 public static async Task NoSwallow(this Task task) where TException : Exception { try { await task; } catch (TException) { var unexpectedEx = task.Exception .Flatten() .InnerExceptions .FirstOrDefault(ex => !(ex is TException)); if (unexpectedEx != null) { throw new NotImplementedException(null, unexpectedEx); } else { throw task.Exception; } } } 

消费代码可能是这样的:

 try { await Task.WhenAll(tasks).NoSwallow(); catch (AggregateException ex) { HandleExceptions(ex); } 

一个骨头exception将具有与同步世界中相同的效果,即使它偶然与MyException同时抛出也是如此。 使用NotIplementedException进行包装有助于不会丢失原始堆栈跟踪。