Task.WhenAny et exceptions non observées
-
12-12-2019 - |
Question
Disons que j'ai trois tâches, a
, b
, et c
.Tous les trois sont assurés de lancer une exception à un moment aléatoire compris entre 1 et 5 secondes.J'écris ensuite le code suivant :
await Task.WhenAny(a, b, c);
Cela finira par générer une exception à la tâche qui échouera en premier.Puisqu'il n'y a pas try...catch
ici, cette exception remontera à un autre endroit dans mon code.
Que se passe-t-il lorsque les deux tâches restantes lèvent une exception ?Ne s’agit-il pas d’exceptions inobservées, qui entraîneront la mort de l’ensemble du processus ?Cela signifie-t-il que la seule façon d'utiliser WhenAny
est à l'intérieur d'un try...catch
bloquer, puis observer d'une manière ou d'une autre les deux tâches restantes avant de continuer ?
Suivi: J'aimerais que la réponse s'applique aux deux à .NET 4.5 et .NET 4.0 avec le pack de ciblage asynchrone (bien qu'utilisant clairement TaskEx.WhenAny
dans ce cas).
La solution
Que se passe-t-il lorsque les deux tâches restantes lèvent une exception ?
Ceux Task
s se terminera dans un état défectueux.
Ne s’agit-il pas d’exceptions inobservées, qui entraîneront la mort de l’ensemble du processus ?
Pas plus.
Dans .NET 4.0, le Task
le destructeur transmettrait son exception non observée à TaskScheduler.UnobservedTaskException
, ce qui mettrait fin au processus s'il n'était pas géré.
Dans .NET 4.5, ceci le comportement a été modifié.Désormais, les exceptions non observées sont transmises à TaskScheduler.UnobservedTaskException
, mais ils sont ensuite ignorés s'ils ne sont pas gérés.
Autres conseils
Oui, les exceptions de tâches restantes ne sont pas observées.Avant .NET 4.5, vous êtes obligé de les respecter (je ne sais pas comment est la situation sur .NET 4.5, mais elle a changé).
J'écris habituellement moi-même une méthode d'assistance pour les tâches de type « déclencher et oublier » comme celles-ci :
public static void IgnoreUnobservedExceptions(this Task task)
{
if (task.IsCompleted)
{
if (task.IsFaulted)
{
var dummy = task.Exception;
}
return;
}
task.ContinueWith(t =>
{
var dummy = t.Exception;
}, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
}
Vous souhaiterez peut-être inclure la connexion dans les applications de production.