Comment empêcher une exception dans un thread d’arrière-plan de mettre fin à une application?

Je peux connecter AppDomain.CurrentDomain.UnhandledException pour enregistrer les exceptions à partir des threads d’arrière-plan, mais comment puis-je les empêcher de terminer le runtime?

En premier lieu, vous devriez vraiment essayer de ne pas créer d’exceptions – et non manipulées – dans un thread d’arrière-plan. Si vous contrôlez la façon dont votre délégué est exécuté, encapsulez-le dans un bloc try catch et tentez de transmettre les informations d’exception à votre thread principal (en utilisant EndInvoke si vous avez explicitement appelé BeginInvoke ou en mettant à jour un état partagé).

Ignorer une exception non gérée peut être dangereux. Si vous avez une véritable exception non manipulable (OutOfMemoryException vient à l’esprit), vous ne pouvez pas faire grand-chose et votre processus est fondamentalement voué à l’échec.

Retour à .Net 1.1, une exception non gérée dans un backgroundthread serait simplement jetée à nulle part et le thread principal serait heureux de labourer. Et cela pourrait avoir des répercussions désagréables. Donc, dans .Net 2.0, ce comportement a changé.

Maintenant, une exception non gérée dans un thread qui n’est pas le thread principal mettra fin au processus. Vous pouvez être averti de cela (en vous inscrivant à l’événement sur l’AppDomain) mais le processus mourra néanmoins.

Comme cela peut être gênant (lorsque vous ne savez pas ce qui sera exécuté dans le thread et que vous n’êtes pas absolument sûr qu’il est correctement protégé, et que votre thread principal doit être résilient), il existe une solution de contournement. Il est conçu comme un paramètre hérité (ce qui signifie que vous devez vous assurer de ne pas avoir de threads parasites), mais vous pouvez forcer le comportement précédent de la manière suivante:

Ajoutez simplement ce paramètre à votre service / application / quel que soit le fichier de configuration:

       

Cela ne semble pas fonctionner avec ASP.NET, cependant.

Pour plus d’informations (et un énorme avertissement indiquant que ce paramètre n’est peut-être pas pris en charge dans les prochaines versions du CLR), voir http://msdn.microsoft.com/en-us/library/ms228965.aspx

De l’excellent article sur le filetage de Joe Albahari:

Le framework .NET fournit un événement de niveau inférieur pour la gestion des exceptions globales: AppDomain.UnhandledException. Cet événement se déclenche lorsqu’il existe une exception non gérée dans un thread et dans tout type d’application (avec ou sans interface utilisateur). Cependant, bien qu’il offre un bon mécanisme de dernier recours pour la journalisation des exceptions non bloquées, il ne fournit aucun moyen d’empêcher l’application de s’éteindre, ni de supprimer la boîte de dialog des exceptions non gérées .NET.

Dans les applications de production, une gestion des exceptions explicite est requirejse sur toutes les méthodes d’entrée de thread. On peut couper le travail en utilisant une classe wrapper ou helper pour effectuer le travail, tel que BackgroundWorker (discuté dans la partie 3).

En gardant la réponse courte, oui, vous pouvez empêcher l’exécution de l’exécution.

Voici une démonstration de la solution de contournement:

 class Program { void Run() { AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); Console.WriteLine("Press enter to exit."); do { (new Thread(delegate() { throw new ArgumentException("ha-ha"); })).Start(); } while (Console.ReadLine().Trim().ToLowerInvariant() == "x"); Console.WriteLine("last good-bye"); } int r = 0; void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Interlocked.Increment(ref r); Console.WriteLine("handled. {0}", r); Console.WriteLine("Terminating " + e.IsTerminating.ToSsortingng()); Thread.CurrentThread.IsBackground = true; Thread.CurrentThread.Name = "Dead thread"; while (true) Thread.Sleep(TimeSpan.FromHours(1)); //Process.GetCurrentProcess().Kill(); } static void Main(ssortingng[] args) { Console.WriteLine("..."); (new Program()).Run(); } } 

Essentiellement, vous n’avez simplement pas laissé le runtime afficher le dialog “… le programme a cessé de fonctionner”.

Si vous avez besoin de journaliser l’exception et de quitter en mode silencieux , vous pouvez appeler Process.GetCurrentProcess().Kill();

Voici un excellent article sur ce problème: Gestion des “exceptions non gérées” dans .NET 2.0

IMO, il serait correct de gérer manuellement les exceptions dans les threads d’arrière-plan et de les relancer via un rappel si nécessaire.

 delegate void ExceptionCallback(Exception ex); void MyExceptionCallback(Exception ex) { throw ex; // Handle/re-throw if necessary } void BackgroundThreadProc(Object obj) { try { throw new Exception(); } catch (Exception ex) { this.BeginInvoke(new ExceptionCallback(MyExceptionCallback), ex); } } private void Test() { ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundThreadProc)); } 
  AppDomain.CurrentDomain.UnhandledException += (sender, e2) => { Thread.CurrentThread.Join(); }; 

Mais attention, ce code gèle toute la mémoire de la stack du thread et de l’object géré du thread lui-même. Cependant, si votre application est dans un état déterminé (peut-être avez-vous lancé une exception LimitedDemoFunctionalityException ou OperationStopWithUserMessageException) et que vous ne développez pas une application 24/7, cette astuce fonctionnera.

Enfin, je pense que MS devrait permettre aux développeurs de remplacer la logique des exceptions non gérées par le haut de la stack.