.Net如何报告在Task.WhenAll()期间抛出的所有异常?

时间:2016-04-01 20:06:43

标签: .net exception-handling async-await

我有一个表单应用程序,在几个点上做

await Task.WhenAll(list_of_tasks)

我的理解是,在这种情况下,如果list_of_tasks中的一个或多个任务抛出异常,那么"等待"也会传播其中一个例外情况。 (但是,您可以检查每项任务,看看它失败的原因)

我的问题是:如果你有一个应用程序范围的异常处理程序,它将所有异常记录到日志中,它如何记录list_of_tasks中抛出的每个异常的信息? (假设异常处理程序只会出现一个从await发出的异常,并且该异常不像AggregateException那样告诉你其他异常)

迈克尔

3 个答案:

答案 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。)