ThreadAbortException

Disons que nous avons un code comme celui-ci en cours d’exécution dans le thread séparé:

private static void ThreadFunc() { ulong counter = 0; while (true) { try { Console.WriteLine( "{0}", counter++ ); } catch (ThreadAbortException) { Console.WriteLine( "Abort!" ); } } } 

Lorsque Thread.Abort() est appelé, est-il possible que l’exception soit renvoyée en dehors du bloc catch?

Oui, une ThreadAbortException est spéciale. Même si vous le manipulez, il sera automatiquement relancé par le CLR à la fin du try / catch / finally. (Comme indiqué dans les commentaires, il peut être supprimé avec ResetAbort mais à ce stade, le code sent le poisson pourri.)

Sans parler du fait qu’il n’existe pas de code exécutable évident en dehors de try / catch / finally, chaque itération de la boucle se termine en dehors de la scope pour une durée limitée, de sorte que l’abandon pourrait se produire en dehors de votre bloc try.

À moins que vous ne fassiez quelque chose dans le bloc catch, je me contenterais d’essayer / finally et de ne pas m’inquiéter de ThreadAbortException . Il existe de bien meilleurs moyens d’interrompre un thread sans utiliser Thread.Abort qui interrompt non seulement de manière chaotique votre code à un point imprévisible, mais ne fonctionne pas non plus car si votre thread appelle actuellement du code non géré, le thread n’abandonnera pas. jusqu’à ce que le contrôle revienne au code managé.

Il est préférable d’utiliser un type de primitive de synchronisation tel qu’un ManualResetEvent pour agir comme indicateur indiquant à votre thread quand quitter. Vous pouvez même utiliser un champ booléen à cet effet, ce que fait BackgroundWorker.

Oui. Je soupçonne que vous vous posez la question, car les interruptions de threads ne se produisent que lorsqu’un thread pourrait bloquer (ou s’il est déjà bloqué), par exemple pour IO.

Il n’y a pas une telle garantie pour avorter. Cela peut se produire à tout moment, bien qu’il y ait des régions de retard telles que des régions d’exécution limitées et des blocs catch / finally, où la demande d’abandon est simplement mémorisée, et le thread est abandonné quand il quitte la région.

L’interruption synchrone du thread (c’est-à-dire l’interruption de votre propre thread) est raisonnablement sûre, mais les avortements asynchrones (annulation d’un thread différent ) sont presque toujours une mauvaise idée. Lisez “Programmation simultanée sous Windows” par Joe Duffy pour plus d’informations.

EDIT: Comme noté par Eric ci-dessous, interrompre un autre thread n’est pas garanti d’avoir un effet non plus. Juste pour citer le commentaire:

J’aurais dit que le thread est interrompu s’il quitte la région, en soulignant que Thread.Abort n’est absolument pas fiable. Un thread qui est interrompu parce qu’il est bloqué dans une boucle infinie ne sera pas interrompu si la boucle se trouve dans une telle région. C’est encore une autre raison pour laquelle Thread.Abort est une mauvaise idée; Si vous ne pouvez pas compter sur l’effet souhaité, pourquoi appelez-vous la méthode?

En réalité, ThreadAbortException est spécial au cas où il serait lancé par la méthode CLR ou Thread.Abort . Comparez la sortie:

  • Exemple légèrement modifié (ajout de Console.WriteLine) de “Concurrent Programming on Windows” de Joe Duffy. Il jette l’exception par Thread.CurrentThread.Abort:
    try { try { Thread.CurrentThread.Abort(); } catch (ThreadAbortException) { Console.WriteLine("First"); //Try to swallow it. } //CLR automatically reraises the exception here . } catch (ThreadAbortException) { Console.WriteLine("Second"); Thread.ResetAbort(); //Try to swallow it again . } //The in - flight abort was reset , so it is not reraised again .
    try { try { Thread.CurrentThread.Abort(); } catch (ThreadAbortException) { Console.WriteLine("First"); //Try to swallow it. } //CLR automatically reraises the exception here . } catch (ThreadAbortException) { Console.WriteLine("Second"); Thread.ResetAbort(); //Try to swallow it again . } //The in - flight abort was reset , so it is not reraised again . 

    Sortie:

     Premier
     Seconde
    
  • Modifiez l’exemple précédent pour utiliser une approche différente de la création d’instance ThreadAbortException:
     try { try { // get non-public constructor var cstor = typeof(ThreadAbortException) .GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); // create ThreadAbortException instance ThreadAbortException ex = cstor.Invoke(null) as ThreadAbortException; // throw.. throw ex; } catch (ThreadAbortException) { Console.WriteLine("First"); } } catch (ThreadAbortException) { Console.WriteLine("Second"); Thread.ResetAbort(); } 

    Sortie:

     Premier
    

Il semble que les méthodes de Thread appellent le code natif en interne, ce qui rend les exceptions spécifiques.

Je ne suis pas à 100% ce que vous demandez, mais je voulais souligner que vous ne pourrez jamais avaler une ThreadAbortException :

Lorsqu’un appel est effectué à la méthode Abort pour détruire un thread, le Common Language Runtime renvoie une ThreadAbortException . ThreadAbortException est une exception spéciale pouvant être interceptée , mais qui sera automatiquement ThreadAbortException à la fin du bloc catch.

Voulez-vous savoir s’il est possible d’attraper une ThreadAbortException qui est lancée dans un autre thread avec un try/catch ? Si c’est votre question, alors non, vous ne pouvez pas.