В этом коде:
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
?
.net
exception
asynchronous
tap
Майкл Рэй Ловетт
источник
источник
AggregateException
. Если бы вы использовалиTask.Wait
вместоawait
в своем примере, вы бы поймалиAggregateException
Task.WhenAll
, и я попал в ту же ловушку. Итак, я попытался подробно рассказать об этом поведении.Ответы:
Я точно не помню, где, но я где-то читал, что с новыми ключевыми словами async / await они разворачивают
AggregateException
фактическое исключение.Итак, в блоке catch вы получаете фактическое исключение, а не агрегированное. Это помогает нам писать более естественный и интуитивно понятный код.
Это также было необходимо для упрощения преобразования существующего кода в использование async / await, когда большая часть кода ожидает определенных исключений, а не агрегированных исключений.
-- Редактировать --
Понял:
Асинхронный учебник Билла Вагнера
источник
Я знаю, что это вопрос, на который уже дан ответ, но выбранный ответ на самом деле не решает проблему OP, поэтому я подумал, что опубликую это.
Это решение дает вам совокупное исключение (то есть все исключения, которые были вызваны различными задачами) и не блокируется (рабочий процесс по-прежнему асинхронный).
Ключ состоит в том, чтобы сохранить ссылку на совокупную задачу, прежде чем вы ее ожидаете, тогда вы можете получить доступ к ее свойству Exception, которое содержит ваше AggregateException (даже если только одна задача вызвала исключение).
Надеюсь, это все еще полезно. Я знаю, что сегодня у меня была эта проблема.
источник
throw task.Exception;
внутрьcatch
блока? (Меня смущает пустой улов, когда на самом деле обрабатываются исключения.)Task.IsCanceled
) не передается должным образом. Это может быть решает , использующие помощника расширения , как это .Вы можете просмотреть все задачи, чтобы увидеть, возникло ли исключение более чем в одной:
источник
WhenAll
завершает работу при первом исключении и возвращает его. см .: stackoverflow.com/questions/6123406/waitall-vs-whenallexceptions
содержит оба выданных исключения.await
вызывает разворачивание первого исключения, но все исключения действительно по-прежнему доступны через массив задач.Просто подумал, что я бы расширил ответ @ Richiban, чтобы сказать, что вы также можете обрабатывать AggregateException в блоке catch, ссылаясь на него из задачи. Например:
источник
Вы думаете о
Task.WaitAll
- выкидываетAggregateException
.WhenAll просто выбрасывает первое исключение из списка обнаруженных исключений.
источник
WhenAll
методом, имеетException
свойство,AggregateException
содержащее все исключения, созданные в егоInnerExceptions
. Здесь происходит то, чтоawait
выбрасывается первое внутреннее исключение вместо самогоAggregateException
себя (как сказал дециклон). ВызовWait
метода задачи вместо его ожидания вызывает исходное исключение.Здесь много хороших ответов, но я все же хотел бы опубликовать свою тираду, поскольку я только что столкнулся с той же проблемой и провел некоторое исследование. Или перейдите к версии TL; DR ниже.
Эта проблема
Ожидание того, что
task
возвращено,Task.WhenAll
вызывает только первое исключение изAggregateException
сохраненного вtask.Exception
, даже если несколько задач дали сбой.В настоящее время документы для
Task.WhenAll
говорят:Это правильно, но ничего не говорится о вышеупомянутом поведении «разворачивания» при ожидании возвращенной задачи.
Я полагаю, что в документации об этом не упоминается, потому что такое поведение не является специфическим для
Task.WhenAll
.Это просто
Task.Exception
тип,AggregateException
и дляawait
продолжений он всегда по замыслу разворачивается как первое внутреннее исключение. Это отлично подходит для большинства случаев, поскольку обычноTask.Exception
состоит только из одного внутреннего исключения. Но рассмотрим этот код:Здесь экземпляр
AggregateException
разворачивается в свое первое внутреннее исключениеInvalidOperationException
точно так же, как мы могли бы это сделатьTask.WhenAll
. Мы могли бы не наблюдать,DivideByZeroException
если бы не прошлиtask.Exception.InnerExceptions
напрямую.Стивен Туб из Microsoft объясняет причину такого поведения в связанной проблеме GitHub :
Еще одна важная вещь, на которую следует обратить внимание, это неглубокое поведение при разворачивании. То есть, он только развернет первое исключение из
AggregateException.InnerExceptions
и оставит его там, даже если это будет экземпляр другогоAggregateException
. Это может добавить еще один слой путаницы. Например, давайте изменимсяWhenAllWrong
так:Решение (TL; DR)
Итак, вернемся к тому
await Task.WhenAll(...)
, что я лично хотел, так это иметь возможность:AggregateException
если одна или несколько задач коллективно сгенерировали более одного исключения;Task
только для проверкиTask.Exception
;Task.IsCanceled
), а что - то вроде этого не будет делать , что:Task t = Task.WhenAll(...); try { await t; } catch { throw t.Exception; }
.Я собрал для этого следующее расширение:
Теперь следующее работает так, как я хочу:
источник
Это работает для меня
источник
WhenAll
не то же самое, чтоWhenAny
.await Task.WhenAny(tasks)
будет завершено, как только любая задача будет завершена. Поэтому, если у вас есть одна задача, которая завершается немедленно и успешно, а другая занимает несколько секунд до выдачи исключения, это немедленно вернется без каких-либо ошибок.В вашем коде первое исключение возвращается конструктивно, как описано на http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5. aspx
Что касается вашего вопроса, вы получите исключение AggreateException, если напишете такой код:
источник