如何在使用Task Parallel Library Task.WhenAny()时处理异常

Hen*_*nke 5 .net c# task-parallel-library async-await c#-5.0

当我使用该Task.WhenAll()函数并在Task中抛出异常时抛出新的AggregateException,我可以捕获它以查看Tasks中发生的所有异常.但是,当我使用时Task.WhenAny()没有抛出任何异常.相反,我必须检查Task.Exception属性值以查看是否发生了异常.这似乎是一个糟糕的代码味道,因为我必须记住Task.Exception每次使用时检查属性Task.WhenAny().难道不应该有更好的方法吗?

这是我的意思的一个例子:

private async void btnMultipleExceptions_Click(object sender, EventArgs e) {
        var task1 = ThrowNotImplementedException();
        var task2 = ThrowDivideByZeroException();

        try {
            Task task = await Task.WhenAny(task1, task2);

            // Even if an exception is thrown in one of the tasks (in our case,
            // task1 will throw first) no exception is thrown from
            // the above await Task.WhenAny(). Instead, the exception is placed on the 
            // Task.Exception property. So I need to check for it every time 
            // I call Task.WhenAny()?
            if (task.Exception != null) {
                Console.WriteLine("Exceptions: " + string.Join(Environment.NewLine,
                    task.Exception.InnerExceptions.Select(x => x.Message).ToArray()));
            } else {
                Console.WriteLine("No Exceptions!");
            }
        } catch(Exception ex) {
            // Try to catch all exceptions???
            AggregateException allEx = ex as AggregateException;

            if (allEx != null) {
                Console.WriteLine("Exceptions: " + string.Join(Environment.NewLine,
                    allEx.InnerExceptions.Select(x => x.Message).ToArray()));
            } else {
                Console.WriteLine("Exceptions: " + ex.Message);
            }
        }
    }

    private async Task ThrowNotImplementedException() {
        await Task.Delay(TimeSpan.FromSeconds(1));
        throw new NotImplementedException();
    }

    private async Task ThrowDivideByZeroException() {
        await Task.Delay(TimeSpan.FromSeconds(2));
        throw new DivideByZeroException();
    }
Run Code Online (Sandbox Code Playgroud)

Ser*_*rvy 3

只是await从返回的任务WhenAny。如果出错,它会解开异常并抛出它,如果没有,你知道它已经完成了,所以你可以继续。

或者,您也可以简单地拨打电话UnwrapTask.WhenAny然后就await可以了。这在语义上与之前的选项相同;唯一的区别在于你认为这或多或少是清楚的。

如果您发现自己非常频繁地解开结果,WhenAny您可以简单地编写自己的实现来为您解开任务,并使用这些实现:

public static Task WhenAny(IEnumerable<Task> tasks)
{
    return Task.WhenAny(tasks).Unwrap();
}
public static Task<T> WhenAny<T>(IEnumerable<Task<T>> tasks)
{
    return Task.WhenAny(tasks).Unwrap();
}
//TODO add wrappers for the `params` overloads if you want them too
Run Code Online (Sandbox Code Playgroud)