.net - programming - ¿Por qué no espera en Task.WhenTodos lanzan una AggregateException?
c# task callback (7)
En este código:
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();});
}
Esperaba que WhenAll
creara y lanzara una AggregateException
, ya que al menos una de las tareas que estaba esperando arrojó una excepción. En cambio, recibo una única excepción lanzada por una de las tareas.
Does WhenAll
no siempre crea una AggregateException
?
En su código, el diseño devuelve la primera excepción como se explica en http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5.aspx
En cuanto a su pregunta, obtendrá la excepción AggreateException si escribe un código como este:
try {
var result = Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2()).Result;
}
catch (Exception ex) {
// Expect AggregateException here
}
Estás pensando en Task.WaitAll
- arroja una AggregateException
.
WhenAll acaba de lanzar la primera excepción de la lista de excepciones que encuentra.
No recuerdo exactamente dónde, pero leí en alguna parte que con las nuevas palabras clave async / await , desenvuelven la AggregateException
en la excepción real.
Entonces, en el bloque catch, obtienes la excepción real y no la agregada. Esto nos ayuda a escribir un código más natural e intuitivo.
Esto también fue necesario para una conversión más fácil del código existente en el uso de async / await, donde muchos códigos esperan excepciones específicas y no excepciones agregadas.
- Editar -
Lo tengo:
Un Async Primer por Bill Wagner
Bill Wagner dijo: (en Cuando ocurren excepciones )
... Cuando utiliza await, el código generado por el compilador desenvuelve la AggregateException y arroja la excepción subyacente. Al aprovechar await, evita el trabajo adicional para manejar el tipo AggregateException utilizado por Task.Result, Task.Wait y otros métodos Wait definidos en la clase Task. Esa es otra razón para usar esperar en lugar de los métodos de tareas subyacentes ....
Pruebe este patrón de código:
Task task = null;
try
{
task = Task.WhenAll(...);
await task;
}
catch (AggregateException aggException)
{
var aggException = task.Exception;
...
}
Puede recorrer todas las tareas para ver si más de uno ha lanzado una excepción:
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(); });
}
Sé que esta es una pregunta que ya ha sido respondida, pero la respuesta elegida realmente no resuelve el problema del PO, así que pensé en publicarlo.
Esta solución le ofrece la excepción agregada (es decir, todas las excepciones que arrojaron las diversas tareas) y no bloquea (el flujo de trabajo sigue siendo asincrónico).
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");
}
La clave es guardar una referencia a la tarea agregada antes de esperarla, luego puede acceder a su propiedad Excepción que contiene su AggregateException (incluso si solo una tarea arrojó una excepción).
Espero que esto todavía sea útil. Sé que tuve este problema hoy.
Solo pensé en expandir la respuesta de @ Richiban para decir que también puedes manejar la AggregateException en el bloque catch al hacer referencia a ella desde la tarea. P.ej:
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");
}