2008-12-09 10 views
8

Tengo una lógica crítica en un bloque finally (con un bloque try vacío), porque quiero garantizar que el código se ejecute incluso si el hilo se cancela. Sin embargo, también me gustaría detectar ThreadAbortException. Descubrí que al envolver mi bloque try/finally crítico en try/catch no se detecta ThreadAbortException. ¿Hay alguna forma de detectarlo?¿Cómo puedo detectar una ThreadAbortException en un bloque finally? (.NET)

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

Respuesta

8

Este es un problema curioso.

El código que ha publicado debe funcionar. Parece que hay algún tipo de optimización que decide no llamar a su controlador de captura.

lo tanto, quería para detectar la excepción con esto:

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

(. Mi código actual simplemente dormía en esa sección de código crítico, por lo que podía estar seguro de que iba a abortar después de que por fin)

Imprimió:

¿Ha abortado el hilo? Falso

¡Hmmm, extraño de hecho!

así que pensé en hacer un poco más trabajo allí, para engañar a las optimizaciones "inteligentes":

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

Dónde AmIEvil es simplemente:

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

Finalmente se imprimen:

¿Hilo abortado? Verdadero

Y ahí lo tienes. Utilizar esto en su código:

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

Dónde NoOp es simplemente:

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

Finalmente no se salta. La 'ThreadAbortException' estaba esperando ser lanzada hasta _después_ de la' captura 'porque el 'try' estaba en un CER (Región de Ejecución Restringida). La llamada al método lo hace tal que no puede ser un CER. –

2
+0

Lo hice, y el único beneficio que proporciona más allá de lo que estoy haciendo actualmente es que prepara todos los recursos antes de ejecutar el bloque finally. – Chris

+0

En realidad, sus CER no son solo para ThreadAbortException. No creo que sea una buena idea escribir cualquier código específico de la aplicación que dependa de la ocurrencia de ThreadAbortException. Y básicamente, finalmente los bloques están ahí para limpiar los recursos, que CERs ayudan a garantizar la ejecución. –

3

En realidad, se puede ejecutar el código en la declaración de catch muy bien para una ThreadAbortException. El problema es que la excepción se volverá a ejecutar una vez que la ejecución abandone el bloque catch.

Si realmente desea evitar que la excepción continúe, puede llamar a Thread.ResetAbort(). Sin embargo, esto requiere confianza total y, a menos que tenga un escenario específico, es casi seguro que no es lo correcto.

ThreadAbortException

+0

Correcto, pero quiero que el código se ejecute independientemente de si se lanza el TAE o no, así que no puedo simplemente poner esta lógica en el controlador TAE, ni tampoco sería limpio implementarlo dos veces. Básicamente, quiero evitar tener que escribir lógica de "retroceso" en mis bloques de captura o finalmente. – Chris

+0

No estoy seguro de entender, tanto la captura y finalmente se ejecutará con una excepción TAE. Simplemente se volverá a lanzar al final de una captura – JaredPar

2

no creo que es posible.

¿Por qué necesita manejar el ThreadAbortException en primer lugar? Llamar a thread.Abort() suele ser un signo de mal diseño. Tener una variable de indicador que cuando se establece en verdadero simplemente devuelve ; de la función de hilo, después de la limpieza adecuada, por supuesto.

De esta manera no tendrá que preocuparse por la excepción.

+0

Haces un buen punto sobre Thread.Abort(), pero rediseñar ese aspecto de nuestra aplicación podría ser demasiado arriesgado en este momento. – Chris

0

Estoy de acuerdo con arul. Calling Thread.Abort() es un signo de mal diseño.

Permítanme citar Peter Ritchie de MSDN: Thread.Abort (el subrayado es mío):

Hay muchas razones para no utilizar Thread.Abort y ThreadAbortException

En ciertas plataformas (como x64 y IA64) del El aborto puede ocurrir antes de que Monitor.Enter y un bloque try (incluso con lock/SyncLock), dejando el monitor huérfano. ThreadAbortException puede ocurrir en un código de terceros no escrito para manejar el aborto de subprocesos. El hilo se puede cancelar mientras se procesa un bloque finally en .NET 1.x Utiliza excepciones para la lógica de flujo de control normal. La excepción asincrónica puede interrumpir la modificación del estado o los recursos del fragmento, dejándolos dañados.

Para más detalles, véase:
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

¿Has probado algo como esto?

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 llama Thread.Abort es un mal diseño ¿por qué llamar a SQL Server en un hilo que ejecuta código de usuario? Porque así es exactamente como se maneja una cancelación de consulta y causa pesadillas.

Cuestiones relacionadas