Почему не ждет в Task.WhenВы бросаете исключение 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
?
Ответы
Ответ 1
Я точно не помню, где, но где-то я читал, что с новыми ключевыми словами async/await они разворачивают AggregateException
в фактическое исключение.
Итак, в блоке catch вы получаете фактическое исключение, а не агрегированное. Это помогает нам написать более естественный и интуитивно понятный код.
Это также необходимо для упрощения преобразования существующего кода в использование async/await, где много кода ожидает определенные исключения и не агрегированные исключения.
- Изменить -
Получил это:
Билл Вагнер сказал: (в Когда Исключения происходят)
... Когда вы используете await, код, сгенерированный компилятором, разворачивает AggregateException и создает основное исключение. Используя подождите, вы избегаете дополнительной работы для обработки типа AggregateException используемые Task.Result, Task.Wait и другие методы ожидания, определенные в Класс задачи. Это еще одна причина, по которой основные методы задачи....
Ответ 2
Вы можете пройти через все задачи, чтобы увидеть, если более чем одна выдает исключение:
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 InvalidOperationException(); });
}
Ответ 3
Я знаю, что это вопрос, который уже ответил, но выбранный ответ на самом деле не решает проблему 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, которое содержит ваше агрегированное исключение (даже если только одна задача выбрала исключение).
Надеюсь, это по-прежнему полезно. Я знаю, что сегодня у меня была эта проблема.
Ответ 4
Вы думаете о Task.WaitAll
- он выдает AggregateException
.
WhenAll просто бросает первое исключение из списка исключений, с которыми он сталкивается.
Ответ 5
Просто подумал, что я бы расширил ответ @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");
}
Ответ 6
В вашем коде первое исключение возвращается дизайном, как описано в 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
}
Ответ 7
Это работает для меня
private async Task WhenAllWithExceptions(params Task[] tasks)
{
var result = await Task.WhenAll(tasks);
if (result.IsFaulted)
{
throw result.Exception;
}
}
Ответ 8
Попробуйте этот код:
Task task = null;
try
{
task = Task.WhenAll(...);
await task;
}
catch (AggregateException aggException)
{
var aggException = task.Exception;
...
}