При возникновении нескольких исключений в вызове Task.WhenAll
создается впечатление, что только одно из исключений включается в задание, как только вы ожидаете его через более чем один уровень ожидания. У меня сложилось впечатление, что свойство Task.Exception.InnerExceptions
будет содержать все возникшие исключения, но при определенных обстоятельствах они, похоже, имеют только одно.
Например, этот пример кода создает несколько задач, генерирующих исключения, и затем ожидает Task.WhenAll на них, а затем записывает в консоль исключения, которые он может перехватить:
class Program
{
static async Task Main(string[] args)
{
var task = CauseMultipleExceptionsAsync();
// Delaying until all the Exceptions have been thrown, ensuring it isn't just a weird race condition happening behind the scenes
await Task.Delay(5000);
try
{
await task;
}
catch(AggregateException e)
{
// This does not get hit
Console.WriteLine($"AggregateException caught: Found {e.InnerExceptions.Count} inner exception(s)");
}
catch(Exception e)
{
Console.WriteLine($"Caught other Exception {e.Message}");
Console.WriteLine($"task.Exception.InnerExceptions contains {task.Exception.InnerExceptions.Count} exception(s)");
foreach (var exception in task.Exception.InnerExceptions)
{
Console.WriteLine($"Inner exception {exception.GetType()}, message: {exception.Message}");
}
}
}
static async Task CauseMultipleExceptionsAsync()
{
var tasks = new List<Task>()
{
CauseExceptionAsync("A"),
CauseExceptionAsync("B"),
CauseExceptionAsync("C"),
};
await Task.WhenAll(tasks);
}
static async Task CauseExceptionAsync(string message)
{
await Task.Delay(1000);
Console.WriteLine($"Throwing exception {message}");
throw new Exception(message);
}
}
Я ожидал, что это либо введет предложение catch(AggregateException e)
, либо, по крайней мере, будет иметь три внутренних исключения в task.Exception.InnerExceptions
- что на самом деле происходит, когда возникает одно единственное исключение, и только * одно исключение находится в task.Exception.InnerExceptions
:
Throwing exception B
Throwing exception A
Throwing exception C
Caught other Exception A
task.Exception.InnerExceptions contains 1 exception(s)
Inner exception System.Exception, message: A
Что еще более странно, так это поведение меняется в зависимости от того, дождитесь вызова Task.WhenAll
в CauseMultipleExceptionsAsync
- если вы возвращаете задачу напрямую, а не ожидаете ее, то все три исключения появляются в task.Exception.InnerException
. Например, замена CauseMultipleExceptionsAsync
на это:
static Task CauseMultipleExceptionsAsync()
{
var tasks = new List<Task>()
{
CauseExceptionAsync("A"),
CauseExceptionAsync("B"),
CauseExceptionAsync("C"),
};
return Task.WhenAll(tasks);
}
Дает этот результат со всеми тремя исключениями, содержащимися в task.Exception.InnerExceptions:
Throwing exception C
Throwing exception A
Throwing exception B
Caught other Exception A
task.Exception.InnerExceptions contains 3 exception(s)
Inner exception System.Exception, message: A
Inner exception System.Exception, message: B
Inner exception System.Exception, message: C
Я очень запутался это - откуда появились исключения B и C go в исходном примере? Как бы вы go обнаружили их снова, если Task.Exception не содержит никакой информации о них? Почему ожидание внутри CauseMultipleExceptionsAsync
скрывает эти исключения, а возврат Task.WhenAll
напрямую не делает?
Если это имеет значение, я могу повторить вышеизложенное в обоих. Net Framework 4.5.2 и. Net Core 2.1.