Почему не ждет на задание.Когда все бросают AggregateException?


в этом коде:

private async void button1_Click(object sender, EventArgs e) {
    try {
        await Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2());
    }
    catch (Exception ex) {
        // Expect AggregateException, but got InvalidTimeZoneException
    }
}

Task DoLongThingAsyncEx1() {
    return Task.Run(() => { throw new InvalidTimeZoneException(); });
}

Task DoLongThingAsyncEx2() {
    return Task.Run(() => { throw new InvalidOperation();});
}

Я ожидал WhenAll чтобы создать и бросить AggregateException, так как по крайней мере одна из задач, которую он ожидал, вызвала исключение. Вместо этого я возвращаю одно исключение, вызванное одной из задач.

тут WhenAll не всегда создают AggregateException?

7 58

7 ответов:

Я точно не помню где, но где-то читал, что с новыми async / await ключевые слова, они разворачивают AggregateException в фактическое исключение.

Итак, в блоке catch вы получаете фактическое исключение, а не агрегированное. Это помогает нам писать более естественный и интуитивно понятный код.

Это также было необходимо для более легкого преобразования существующего кода в использование async / await где много кода ожидает конкретных исключений, а не агрегированные исключения.

-- Edit --

есть:

асинхронный праймер Билла Вагнера

Билл Вагнер сказал: (in Когда Происходят Исключения)

...При использовании await код, сгенерированный компилятором, разворачивает AggregateException и создает базовое исключение. За счет использования ожидайте, вы избегаете дополнительной работы для обработки типа AggregateException используемый по задачам.Результат, Задача.Подождите, и другие методы ожидания, определенные в Класс задач. Это еще одна причина использовать await вместо основные методы задачи....

вы можете пройти все задачи, чтобы увидеть, если более чем один вызвал исключение:

private async Task Example()
{
    var tasks = new [] { DoLongThingAsyncEx1(), DoLongThingAsyncEx2() };

    try 
    {
        await Task.WhenAll(tasks);
    }
    catch (Exception ex) 
    {
        var exceptions = tasks.Where(t => t.Exception != null)
                              .Select(t => t.Exception);
    }
}

private Task DoLongThingAsyncEx1()
{
    return Task.Run(() => { throw new InvalidTimeZoneException(); });
}

private Task DoLongThingAsyncEx2()
{
    return Task.Run(() => { throw new InvalidOperation(); });
}

Я знаю, что это вопрос, на который уже ответили, но выбранный ответ не действительно решите проблему OP, поэтому я подумал, что опубликую это.

Это решение дает вам агрегатное исключение (т. е. все исключения, которые были брошены различные задачи) и не блокирует (рабочий процесс асинхронной).

async Task Main()
{
    var task = Task.WhenAll(A(), B());

    try
    {
        var results = await task;
        Console.WriteLine(results);
    }
    catch (Exception)
    {
    }

    if (task.Exception != null)
    {
        throw task.Exception;
    }
}

public async Task<int> A()
{
    await Task.Delay(100);
    throw new Exception("A");
}

public async Task<int> B()
{
    await Task.Delay(100);
    throw new Exception("B");
}

ключ должен сохранить ссылку на агрегатную задачу, прежде чем ждать ее, тогда вы можете получить доступ к ее Свойство Exception, которое содержит ваше AggregateException (даже если только одна задача вызвала исключение).

надеюсь, что это все-таки полезно. Я знаю, что у меня была эта проблема сегодня.

ты думал о Task.WaitAll - Он выдает AggregateException.

WhenAll просто выбрасывает первое исключение из списка исключений, с которыми он сталкивается.

просто подумал, что я бы расширил ответ @Richiban, чтобы сказать, что вы также можете обрабатывать исключение AggregateException в блоке catch, ссылаясь на него из задачи. Например:

async Task Main()
{
    var task = Task.WhenAll(A(), B());

    try
    {
        var results = await task;
        Console.WriteLine(results);
    }
    catch (Exception ex)
    {
        // This doesn't fire until both tasks
        // are complete. I.e. so after 10 seconds
        // as per the second delay

        // The ex in this instance is the first
        // exception thrown, i.e. "A".
        var firstExceptionThrown = ex;

        // This aggregate contains both "A" and "B".
        var aggregateException = task.Exception;
    }
}

public async Task<int> A()
{
    await Task.Delay(100);
    throw new Exception("A");
}

public async Task<int> B()
{
    // Extra delay to make it clear that the await
    // waits for all tasks to complete, including
    // waiting for this exception.
    await Task.Delay(10000);
    throw new Exception("B");
}

в вашем коде первое исключение возвращается по дизайну, как описано в разделе http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5.aspx

Что касается вашего вопроса, Вы получите исключение AggreateException, если вы напишете такой код:

try {
    var result = Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2()).Result; 
}
catch (Exception ex) {
    // Expect AggregateException here
} 

попробуйте этот шаблон код:

Task task = null;
try
{
    task = Task.WhenAll(...);
    await task;
 }
 catch (AggregateException aggException)
 {
     var aggException = task.Exception;
     ...
 }