Почему не ждет на задание.Когда все бросают 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 ответов:
Я точно не помню где, но где-то читал, что с новыми 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 }