Dans ce code:
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();});
}
Je m'attendais WhenAll
à créer et à lancer un AggregateException
, car au moins l'une des tâches qu'il attendait a lancé une exception. Au lieu de cela, je récupère une seule exception lancée par l'une des tâches.
Ne WhenAll
crée pas toujours un AggregateException
?
.net
exception
asynchronous
tap
Michael Ray Lovett
la source
la source
AggregateException
. Si vous avez utiliséTask.Wait
au lieu deawait
dans votre exemple, vous attraperiezAggregateException
Task.WhenAll
et je suis tombé dans le même piège. J'ai donc essayé d' entrer dans les détails sur ce comportement.Réponses:
Je ne me souviens pas exactement où, mais j'ai lu quelque part qu'avec les nouveaux mots-clés async / await , ils déploient le
AggregateException
dans l'exception réelle.Ainsi, dans le bloc catch, vous obtenez l'exception réelle et non l'agrégée. Cela nous aide à écrire du code plus naturel et intuitif.
Cela était également nécessaire pour faciliter la conversion du code existant en utilisant async / await où le grand nombre de code attend des exceptions spécifiques et non des exceptions agrégées.
-- Éditer --
Je l'ai:
An Async Primer par Bill Wagner
la source
Je sais que c'est une question à laquelle on a déjà répondu, mais la réponse choisie ne résout pas vraiment le problème du PO, alors j'ai pensé que je publierais ceci.
Cette solution vous donne l'exception globale (c'est-à-dire toutes les exceptions qui ont été levées par les différentes tâches) et ne bloque pas (le flux de travail est toujours asynchrone).
La clé est d'enregistrer une référence à la tâche d'agrégation avant de l'attendre, vous pouvez alors accéder à sa propriété Exception qui contient votre AggregateException (même si une seule tâche a levé une exception).
J'espère que cela est toujours utile. Je sais que j'ai eu ce problème aujourd'hui.
la source
throw task.Exception;
intérieur ducatch
bloc? (Cela me déroute de voir une capture vide lorsque les exceptions sont réellement gérées.)Task.IsCanceled
) n'est pas correctement propagé. Cela peut être résolu en utilisant un assistant d'extension comme celui-ci .Vous pouvez parcourir toutes les tâches pour voir si plusieurs d'entre elles ont généré une exception:
la source
WhenAll
quitte à la première exception et retourne cela. voir: stackoverflow.com/questions/6123406/waitall-vs-whenallexceptions
contient les deux exceptions levées.await
provoque le déballage de la première exception, mais toutes les exceptions sont en effet toujours disponibles via le tableau de tâches.Je pensais juste que je développerais la réponse de @ Richiban pour dire que vous pouvez également gérer l'exception AggregateException dans le bloc catch en le référençant à partir de la tâche. Par exemple:
la source
Vous pensez à
Task.WaitAll
- cela lance unAggregateException
.WhenAll lève simplement la première exception de la liste des exceptions qu'il rencontre.
la source
WhenAll
méthode a uneException
propriété quiAggregateException
contient toutes les exceptions levées dans sonInnerExceptions
. Ce qui se passe ici, c'est queawait
jeter la première exception intérieure au lieu deAggregateException
elle - même (comme dit le décyclone). L'appel de laWait
méthode de la tâche au lieu de l'attendre entraîne la levée de l'exception d'origine.Beaucoup de bonnes réponses ici, mais j'aimerais tout de même publier ma diatribe car je viens de rencontrer le même problème et j'ai mené des recherches. Ou passez à la version TLDR ci-dessous.
Le problème
Attendre le
task
retour parTask.WhenAll
lève uniquement la première exception duAggregateException
stocké danstask.Exception
, même lorsque plusieurs tâches ont échoué.La documentation actuelle pour
Task.WhenAll
dire:Ce qui est correct, mais cela ne dit rien sur le comportement de "déroulement" mentionné ci-dessus lorsque la tâche retournée est attendue.
Je suppose que les documents ne le mentionnent pas parce que ce comportement n'est pas spécifique à
Task.WhenAll
.C'est simplement ce qui
Task.Exception
est de typeAggregateException
et pour lesawait
suites, il est toujours déballé comme sa première exception intérieure, par conception. C'est génial dans la plupart des cas, car il seTask.Exception
compose généralement d'une seule exception interne. Mais considérez ce code:Ici, une instance de
AggregateException
est déroulée dans sa première exception interneInvalidOperationException
exactement de la même manière que nous l'aurions pu avoir avecTask.WhenAll
. Nous aurions pu ne pas observerDivideByZeroException
si nous ne passions pastask.Exception.InnerExceptions
directement.Stephen Toub de Microsoft explique la raison de ce comportement dans le problème GitHub associé :
Une autre chose importante à noter, ce comportement de déballage est peu profond. C'est-à-dire qu'il ne déroulera que la première exception
AggregateException.InnerExceptions
et la laissera là, même s'il s'agit d'une instance d'une autreAggregateException
. Cela peut ajouter encore une autre couche de confusion. Par exemple, changeonsWhenAllWrong
comme ceci:Une solution (TLDR)
Donc, revenons à
await Task.WhenAll(...)
, ce que je voulais personnellement, c'est pouvoir:AggregateException
si plus d'une exception a été levée collectivement par une ou plusieurs tâches;Task
seul pour vérifier sonTask.Exception
;Task.IsCanceled
), comme quelque chose comme cela ne ferait pas cela:Task t = Task.WhenAll(...); try { await t; } catch { throw t.Exception; }
.J'ai mis en place l'extension suivante pour cela:
Maintenant, ce qui suit fonctionne comme je le souhaite:
la source
Cela fonctionne pour moi
la source
WhenAll
n'est pas la même chose queWhenAny
.await Task.WhenAny(tasks)
s'achèvera dès qu'une tâche sera terminée. Donc, si vous avez une tâche qui se termine immédiatement et qui réussit et qu'une autre prend quelques secondes avant de lancer une exception, elle reviendra immédiatement sans aucune erreur.Dans votre code, la première exception est renvoyée par conception, comme expliqué à l' adresse http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5. aspx
Quant à votre question, vous obtiendrez l'exception AggreateException si vous écrivez du code comme celui-ci:
la source