2008-12-09 3 views
8

J'ai une logique critique dans un bloc finally (avec un bloc try vide), car je veux garantir que le code est exécuté même si le thread est abandonné. Cependant, j'aimerais aussi détecter l'exception ThreadAbortException. J'ai trouvé qu'encapsuler mon bloc try/finally critique dans un try/catch n'attrape pas l'exception ThreadAbortException. Y a-t-il un moyen de le détecter?Comment puis-je détecter une exception ThreadAbortException dans un bloc finally? (.NET)

 
try { 
    try { } 
    finally { 
     // critical logic 
    } 
} catch(Exception ex) { 
    // ThreadAbortException is not caught here, but exceptions thrown 
    // from within the critical logic are 
} 

Répondre

8

Ceci est un problème curieux.Le code que vous avez publié doit. Il semble qu'il y ait une sorte d'optimisation qui décide de ne pas appeler votre gestionnaire de prises.

Donc, je voulais détecter l'exception avec ceci:

bool threadAborted = true; 
try { 
    try { } 
    finally { /* critical code */ } 
    threadAborted = false; 
} 
finally { 
    Console.WriteLine("Thread aborted? {0}", threadAborted); 
} 
Console.WriteLine("Done"); 

(. Mon code actuel juste dormi dans cette section de code critique, pour que je puisse être sûr qu'il serait abandonner après enfin)

Il a imprimé:

Fil annulé? Faux

Hmmm, étrange en effet!

Je pensais à faire un peu plus de travail là-bas, pour tromper les optimisations "intelligentes":

bool threadAborted = true; 
try { 
    try { } 
    finally { /* critical code */ } 
    threadAborted = AmIEvil(); 
} 
finally { 
    Console.WriteLine("Thread aborted? {0}", threadAborted); 
} 
Console.WriteLine("Done"); 

AmIEvil est juste:

[MethodImpl(MethodImplOptions.NoInlining)] 
static bool AmIEvil() { 
    return false; 
} 

Enfin, il imprimé:

Fil annulé? Vrai

Et voilà. Utilisez dans votre code:

try { 
    try { } 
    finally { /* critical code */ } 
    NoOp(); 
} 
catch (Exception ex) { 
    // ThreadAbortException is caught here now! 
} 

NoOp est juste:

[MethodImpl(MethodImplOptions.NoInlining)] 
static void NoOp() { } 
+0

Le finalement n'est pas ignoré. Le 'ThreadAbortException' attendait d'être jeté jusqu'à _after_ le' catch' car le 'try' était dans une CER (Contrained Execution Region). L'appel de méthode le rend tel qu'il ne peut pas être un CER. –

2

En savoir plus sur Constrained Execution Regions.

Plus précisément, le RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup method sera utile ici.

+0

Je l'ai fait, et le seul avantage qu'il offre au-delà de ce que je fais actuellement, c'est qu'il prépare toutes les ressources avant d'exécuter le bloc finally. – Chris

+0

En fait, vous CERs ne sont pas seulement pour ThreadAbortException. Je ne pense pas que ce soit une bonne idée d'écrire un code spécifique à l'application qui repose sur l'occurrence de ThreadAbortException. Et fondamentalement, enfin, des blocs sont là pour nettoyer les ressources, ce que les URCE aident à garantir l'exécution. –

3

Vous pouvez réellement exécuter du code dans l'instruction catch juste pour une exception ThreadAbortException. Le problème est que l'exception sera renvoyée lorsque l'exécution quittera le bloc catch.

Si vous voulez réellement empêcher l'exception de continuer, vous pouvez appeler Thread.ResetAbort(). Cela nécessite une confiance totale et si vous n'avez pas de scénario spécifique, c'est certainement la mauvaise chose à faire.

ThreadAbortException

+0

Bien, mais je veux que le code s'exécute indépendamment du fait que le TAE soit levé ou pas, donc je ne peux pas simplement mettre cette logique dans le gestionnaire TAE, et il ne serait pas propre à l'implémenter deux fois. Fondamentalement, je veux éviter d'avoir à écrire une logique de "back-out" dans mes prises ou finalement bloquer. – Chris

+0

Je ne suis pas sûr de comprendre, à la fois le catch et finalement courir avec une exception TAE. Il sera juste republié à la fin d'une capture – JaredPar

2

Je ne pense pas que ce soit possible. Pourquoi avez-vous besoin de gérer le ThreadAbortException en premier lieu? L'appel thread.Abort() est généralement un signe de mauvaise conception. Avoir une variable d'indicateur qui, lorsqu'elle est définie sur true, retournera simplement ; de la fonction de fil, après un nettoyage approprié bien sûr.

De cette façon, vous n'aurez pas à vous soucier de l'exception.

+0

Vous faites un bon point sur Thread.Abort(), mais la refonte de cet aspect de notre application pourrait être trop risquée à ce stade. – Chris

0

Je suis d'accord avec arul. L'appel de Thread.Abort() est un signe de mauvaise conception.

Permettez-moi de citer Peter Ritchie de MSDN: Thread.Abort (l'accent est à moi):

Il y a de nombreuses raisons de ne pas utiliser Thread.Abort et ThreadAbortException

Sur certaines plates-formes (comme x64 et IA64) la abort peut se produire avant Monitor.Enter et un bloc try (même avec lock/SyncLock), laissant le moniteur orphelin. L'exception ThreadAbortException peut se produire dans un code tiers non écrit pour gérer l'abandon de thread. Le thread peut être abandonné lors du traitement d'un bloc finally dans .NET 1.x Utilise des exceptions pour la logique de flux de contrôle normal. Une exception asynchrone peut interrompre la modification de l'état de fragment ou des ressources, les laissant corrompues.

Pour plus de détails, voir:
http://msmvps.com/blogs/peterritchie/archive/2007/08/22/thead-abort-is-a-sign-of-a-poorly-designed-program.aspx
http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
http://blogs.msdn.com/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx

0

Avez-vous essayé quelque chose comme ça?

try { 
    try { } 
    catch (ThreadAbortException) 
    { 
     ThreadAbortExceptionBool = true; 
    } 
    finally { 
     // critical logic 
     if (ThreadAbortExceptionBool) 
      // Whatever 
    } 
} 
catch(Exception ex) { 
    // ThreadAbortException is not caught here, but exceptions thrown 
    // from within the critical logic are 
} 
2

Si vous appelez Thread.Abort est mauvaise conception pourquoi appeler SQL Server sur un thread qui exécute le code utilisateur? Parce que c'est exactement la manière dont une requête d'annulation est traitée et cela provoque des cauchemars.

Questions connexes