我有一个表单应用程序,在几个点上做
await Task.WhenAll(list_of_tasks)
我的理解是,在这种情况下,如果list_of_tasks中的一个或多个任务抛出异常,那么"等待"也会传播其中一个例外情况。 (但是,您可以检查每项任务,看看它失败的原因)
我的问题是:如果你有一个应用程序范围的异常处理程序,它将所有异常记录到日志中,它如何记录list_of_tasks中抛出的每个异常的信息? (假设异常处理程序只会出现一个从await发出的异常,并且该异常不像AggregateException那样告诉你其他异常)
迈克尔答案 0 :(得分:2)
因此您可能已经知道了,为了方便起见,等待解包AggregateException,因为Task.Exception属性是AggregateException类型,如果它没有解包 - 在等待时你必须总是捕获AggregateException一些任务,然后打扰自己解开。
使用Task.WhenAll我认为这种行为确实不是你想要的。所有其他异常都被吞下(因此被认为是观察过的,并且不会获得UnobservedTaskException处理程序)。如果在这种情况下记录所有异常很重要,可以编写一些扩展方法,如下所示:
static class TaskExtensions {
static async Task WhenAllAggregate(params Task[] tasks) {
var all = Task.WhenAll(tasks);
try {
await all;
}
catch {
// swallow caugth exception but throw all
throw all.Exception;
}
}
}
然后像往常一样在应用程序范围的处理程序中记录聚合异常。
答案 1 :(得分:1)
答案https://stackoverflow.com/a/36365489/340035非常好。
我唯一要改变的是字符数量并使用以下内容:
await Task.WhenAll(tasks).ContinueWith(t => t.Wait());
您可以在这篇精彩的帖子中找到有关异常处理决策的更多信息: https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/
答案 2 :(得分:0)
Jon Skeet提供a sketch for a solution作为扩展方法:
public static Task PreserveMultipleExceptions(this Task originalTask)
{
var tcs = new TaskCompletionSource<object>();
originalTask.ContinueWith(task => {
switch (task.Status) {
case TaskStatus.Canceled:
tcs.SetCanceled();
break;
case TaskStatus.RanToCompletion:
tcs.SetResult(null);
break;
case TaskStatus.Faulted:
tcs.SetException(originalTask.Exception);
break;
}
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
你可以使用:
var whenAllTask = Task.WhenAll(tasks).PreserveMultipleExceptions();
await whenAllTask; // Note that the exception isn't thrown until the task has been awaited!
或者如果您愿意(在当前缺乏类型扩展方法的情况下):
public static Task WhenAllPreserveMultipleExceptions(this IEnumerable<Task> tasks)
{
var aggregateTask = Task.WhenAll(tasks);
var tcs = new TaskCompletionSource<object>();
aggregateTask.ContinueWith(task => {
switch (task.Status)
{
case TaskStatus.Canceled:
tcs.SetCanceled();
break;
case TaskStatus.RanToCompletion:
tcs.SetResult(null);
break;
case TaskStatus.Faulted:
tcs.SetException(aggregateTask.Exception);
break;
}
}, TaskContinuationOptions. ExecuteSynchronously);
return tcs.Task;
}
你可以使用:
var whenAllTask = tasks.WhenAllPreserveMultipleExceptions();
await whenAllTask; // Note that the exception isn't thrown until the task has been awaited!
(currently accepted solution的一个可能问题是它会在调用方法时抛出,而不是在等待它时可能不是你想要的,并且不会模仿Task.WhenAll。)