Deben evitarse los hilos de aborto e interrupción si es posible, ya que esto puede dañar el estado de un programa en ejecución. Por ejemplo, imagine que abortó un hilo que mantenía bloqueos abiertos a los recursos, estos bloqueos nunca se liberarían.
En lugar considerar el uso de un mecanismo de señalización de modo que las roscas pueden cooperar entre sí y así manejar el bloqueo y desbloqueo de gracia, por ejemplo:
private readonly AutoResetEvent ProcessEvent = new AutoResetEvent(false);
private readonly AutoResetEvent WakeEvent = new AutoResetEvent(false);
public void Do()
{
Thread th1 = new Thread(ProcessSomething);
th1.IsBackground = false;
th1.Start();
ProcessEvent.WaitOne();
Console.WriteLine("Processing started...");
Thread th2 = new Thread(() => WakeEvent.Set());
th2.Start();
th1.Join();
Console.WriteLine("Joined");
}
private void ProcessSomething()
{
try
{
Console.WriteLine("Processing...");
ProcessEvent.Set();
}
finally
{
WakeEvent.WaitOne();
Console.WriteLine("Woken up...");
}
}
actualización
Todo un bajo nivel interesante problema. Aunque Abort()
está documentado, Interrupt()
es mucho menos.
La respuesta breve a su pregunta es no, no puede reactivar un hilo en un bloque finally llamando al Abort
o Interrupt
en él.
No poder abortar o interrumpir los hilos en los bloques finally es por diseño, simplemente para que finalmente los bloques tengan la oportunidad de ejecutarse como era de esperar. Si pudieras abortar e interrumpir los hilos en bloques finalmente, esto podría tener consecuencias no deseadas para las rutinas de limpieza, y así dejar la aplicación en un estado dañado, no es bueno.
Un ligero matiz con la interrupción del hilo, es que se puede haber emitido una interrupción contra el hilo en cualquier momento antes de entrar en el bloque finally, pero mientras no estaba en un estado SleepWaitJoin
(es decir, no bloqueado). En este caso, si había una llamada de bloqueo en el bloque finally, lanzaría inmediatamente un ThreadInterruptedException
y se bloquearía el bloque finally. Finalmente la protección del bloque lo impide.
Además de protección en bloques finally, esto se extiende para probar bloques y también CERs (
Constrained Execution Region) que se pueden configurar en código de usuario para evitar que se genere un rango de excepciones hasta después de la ejecución de una región, muy útil para bloques críticos del código que
debe se completa y demora el aborto.
La excepción (sin juego de palabras) a esto es lo que se llama Rude Aborts. Estos son ThreadAbortExceptions
planteados por el entorno de alojamiento CLR en sí. Estos pueden provocar que se salgan los bloques finally y catch, pero no CERs. Por ejemplo, el CLR puede generar Abortos groseros en respuesta a los hilos que ha tardado demasiado en realizar su trabajo \ exit, p. cuando intenta descargar un AppDomain o ejecutar código dentro de SQL Server CLR. En su ejemplo particular, cuando su aplicación se apaga y el AppDomain se descarga, el CLR emitiría un aborto brusco en el hilo de dormir ya que habría un tiempo de espera de descarga de AppDomain.
Abortar e interrumpir en bloques finalmente no va a suceder en el código de usuario, pero hay un comportamiento ligeramente diferente entre los dos casos.
Abortar
Al llamar Abort
en un hilo en un bloque finally, el subproceso de llamada se bloquea. Esta es documented:
El subproceso que llama Abortar podría bloquear si el hilo que está siendo abortado se encuentra en una zona protegida de código, como un bloque catch, finally, o región ejecución limitada.
En el caso de abortar si el sueño no era infinita:
- El subproceso de llamada emitirá una
Abort
pero bloquear aquí hasta que se sale del bloque finally es decir que se detenga aquí no se detiene inmediatamente a la declaración Join
.
- El hilo de llamada tiene su estado establecido en
AbortRequested
.
- El destinatario continúa durmiendo.
- Cuando el destinatario se despierta, ya que tiene un estado de
AbortRequested
, continuará ejecutando el código de bloque finalmente y luego se "evaporará", es decir, saldrá.
- Cuando la hebra abortada abandona el bloque finally: no se produce ninguna excepción, no se ejecuta ningún código después de la ejecución del bloque finally, y el estado del subproceso es
Aborted
.
- El subproceso de llamada está desbloqueado, continúa con la instrucción
Join
y pasa inmediatamente cuando el subproceso llamado ha salido.
Así que dado el ejemplo con un sueño infinito, el subproceso de llamada se bloqueará para siempre en el paso 1.
interrupción
En el caso de interrupción si el sueño no era infinita:
No tan bien documentado ...
- El subproceso de llamada emitirá una
Interrupt
y continuar de ejecución.
- El hilo de llamada se bloqueará en la instrucción
Join
.
- El hilo de llamada entrante tiene su estado configurado para generar una excepción en la siguiente llamada de bloqueo, pero lo más importante es que está desbloqueado, es decir, como desbloqueado, es decir, despertado.
- El destinatario continúa durmiendo.
- Cuando el destinatario se despierta, continuará ejecutando el bloque finally.
- Cuando el hilo interrumpido abandona el bloque finally lanzará un
ThreadInterruptedException
en su siguiente llamada de bloqueo (ver ejemplo de código a continuación).
- El subproceso de la llamada "une" y continúa a medida que el hilo se llama ha salido, sin embargo, el no controlada
ThreadInterruptedException
en el paso 6 se ha aplanado ahora el proceso ...
Así que de nuevo da el ejemplo con un sueño infinito, el subproceso de llamada se bloqueará para siempre, pero en el paso 2.
Resumen
Así que, aunque Abort
y Interrupt
tener un comportamiento ligeramente diferente, que van tanto resultado en el hilo llamado dormir para siempre, y el subproceso de llamada bloqueo para siempre (en tu ejemplo).
Sólo un grosero Abortar puede forzar un hilo bloqueado para salir de un bloque finally, y éstos sólo pueden ser planteada por la propia CLR (ni siquiera se puede utilizar la reflexión para diddle ThreadAbortException.ExceptionState
, ya que hace una llamada CLR interna para obtener el AbortReason
- no hay oportunidad de ser fácilmente malvado allí ...).
El CLR evita que el código de usuario haga que los bloques finalmente se eliminen prematuramente por nuestro propio bien, ayuda a prevenir el estado dañado.
Para un ejemplo del comportamiento ligeramente diferente con Interrupt
:
internal class ThreadInterruptFinally
{
public static void Do()
{
Thread t = new Thread(ProcessSomething) { IsBackground = false };
t.Start();
Thread.Sleep(500);
t.Interrupt();
t.Join();
}
private static void ProcessSomething()
{
try
{
Console.WriteLine("processing");
}
finally
{
Thread.Sleep(2 * 1000);
}
Console.WriteLine("Exited finally...");
Thread.Sleep(0); //<-- ThreadInterruptedException
}
}
No sé la respuesta a la pregunta, pero I * no * sé que, como regla general, evito 'interrupción () 'como un mecanismo para la señalización entre hilos. Hay muchas más API predecibles: 'Monitor',' {Manual | Auto} ResetEvent', etc. –
Estaba buscando más explicaciones/documentación cuando Interrupt/Abort no funciona finalmente y por qué MSDN dice lo contrario. Mi ejemplo es un tipo de sondeo más complejo y ahora lo estoy leyendo en waithandles como se sugiere. – Marek
Hay dos tipos de abortos de hilos. El amigable no interrumpirá el código en un bloque final. El antipático se invoca cuando el proceso se cierra en un subproceso que tiene IsBackground igual a verdadero o cuando termina en una excepción no controlada. Ahora no importa que el código haya sido interrumpido bruscamente. –