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 WhenAll
crear y lanzar un AggregateException
, ya que al menos una de las tareas que estaba esperando arrojó una excepción. En cambio, obtengo una única excepción lanzada por una de las tareas.
¿ WhenAll
No siempre crea una AggregateException
?
.net
exception
asynchronous
tap
Michael Ray Lovett
fuente
fuente
AggregateException
. Si usara enTask.Wait
lugar deawait
en su ejemplo, lo atraparíaAggregateException
Task.WhenAll
y caí en la misma trampa. Así que intenté entrar en detalles profundos sobre este comportamiento.Respuestas:
No recuerdo exactamente dónde, pero leí en alguna parte que con las nuevas palabras clave async / await , desenvuelven la
AggregateException
excepción real.Entonces, en el bloque de captura, obtiene la excepción real y no la agregada. Esto nos ayuda a escribir código más natural e intuitivo.
Esto también era necesario para facilitar la conversión del código existente en el uso de async / await donde una gran cantidad de código espera excepciones específicas y no excepciones agregadas.
- Editar -
Entendido:
Una cartilla asincrónica de Bill Wagner
fuente
Sé que esta es una pregunta que ya está respondida, pero la respuesta elegida realmente no resuelve el problema del OP, así que pensé en publicar esto.
Esta solución le brinda la excepción agregada (es decir, todas las excepciones generadas por las diversas tareas) y no bloquea (el flujo de trabajo sigue siendo asincrónico).
La clave es guardar una referencia a la tarea agregada antes de esperarla, luego puede acceder a su propiedad Exception que contiene su AggregateException (incluso si solo una tarea arrojó una excepción).
Espero que esto siga siendo útil. Sé que hoy tuve este problema.
fuente
throw task.Exception;
interior delcatch
bloque? (Me confunde ver una captura vacía cuando las excepciones realmente se están manejando.)Task.IsCanceled
) no se propaga correctamente. Esto se puede resolver usando un ayudante de extensión como este .Puede recorrer todas las tareas para ver si más de una ha generado una excepción:
fuente
WhenAll
sale con la primera excepción y devuelve eso. ver: stackoverflow.com/questions/6123406/waitall-vs-whenallexceptions
contiene ambas excepciones lanzadas.await
hace que se desenvuelva la primera excepción, pero todas las excepciones todavía están disponibles a través de la matriz de tareas.Solo pensé en ampliar la respuesta de @ Richiban para decir que también puede manejar la excepción AggregateException en el bloque catch haciendo referencia a ella desde la tarea. P.ej:
fuente
Estás pensando en
Task.WaitAll
... arroja unAggregateException
.WhenAll simplemente lanza la primera excepción de la lista de excepciones que encuentra.
fuente
WhenAll
método tiene unaException
propiedad queAggregateException
contiene todas las excepciones lanzadas en suInnerExceptions
. Lo que está sucediendo aquí esawait
lanzar la primera excepción interna en lugar de laAggregateException
propia (como dijo Decyclone). Llamar alWait
método de la tarea en lugar de esperarlo hace que se lance la excepción original.Muchas buenas respuestas aquí, pero aún me gustaría publicar mi perorata, ya que me encontré con el mismo problema y realicé algunas investigaciones. O pase a la versión TLDR a continuación.
El problema
Esperar la
task
devoluciónTask.WhenAll
solo arroja la primera excepción de laAggregateException
almacenadatask.Exception
, incluso cuando varias tareas han fallado.Los documentos actuales para
Task.WhenAll
decir:Lo cual es correcto, pero no dice nada sobre el comportamiento de "desenvolvimiento" antes mencionado de cuándo se espera la tarea devuelta.
Supongo que los documentos no lo mencionan porque ese comportamiento no es específico de
Task.WhenAll
.Es simplemente que
Task.Exception
es de tipoAggregateException
y para lasawait
continuaciones siempre se desenvuelve como su primera excepción interna, por diseño. Esto es excelente para la mayoría de los casos, porque generalmenteTask.Exception
consta de una sola excepción interna. Pero considere este código:Aquí, una instancia de
AggregateException
se desenvuelve en su primera excepción internaInvalidOperationException
exactamente de la misma manera que podríamos haberla tenidoTask.WhenAll
. Podríamos haber dejado de observarDivideByZeroException
si no hubiéramos pasadotask.Exception.InnerExceptions
directamente.Stephen Toub de Microsoft explica la razón detrás de este comportamiento en el problema relacionado de GitHub :
Otra cosa importante a tener en cuenta, este comportamiento de desenvolver es superficial. Es decir, solo desenvolverá la primera excepción de
AggregateException.InnerExceptions
y la dejará allí, incluso si resulta ser una instancia de otraAggregateException
. Esto puede agregar otra capa de confusión. Por ejemplo, cambiemosWhenAllWrong
así:Una solución (TLDR)
Entonces, volviendo a
await Task.WhenAll(...)
, lo que personalmente quería es poder:AggregateException
si se ha lanzado más de una excepción de forma colectiva por una o más tareas;Task
único para comprobarloTask.Exception
;Task.IsCanceled
), ya que algo como esto no haría eso:Task t = Task.WhenAll(...); try { await t; } catch { throw t.Exception; }
.He reunido la siguiente extensión para eso:
Ahora, lo siguiente funciona de la manera que quiero:
fuente
Esto funciona para mi
fuente
WhenAll
no es lo mismo queWhenAny
.await Task.WhenAny(tasks)
se completará tan pronto como se complete cualquier tarea. Entonces, si tiene una tarea que se completa de inmediato y tiene éxito y otra toma unos segundos antes de lanzar una excepción, esta volverá inmediatamente sin ningún error.En su código, la primera excepción se devuelve por diseño, 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 AggreateException si escribe un código como este:
fuente