2011-04-25 13 views
6

Según tengo entendido, Thread.Abort debe generar una ThreadAbortException en un hilo bloqueado, sin embargo, este no parece ser el caso cuando se trata de TcpListener.AcceptSocket. Aquí está el ejemplo más básico de la cuestión:AcceptSocket no respeta un Thread.Abort request

class Program 
{ 
    static void Main(string[] args) 
    { 
     Thread thread = new Thread(Listen); 
     thread.Start(); 
     Thread.Sleep(1000); // give it a second to get going 
     Console.WriteLine("Aborting listener thread"); 
     thread.Abort(); 
     thread.Join(); 
     Console.WriteLine("Listener thread finished press <enter> to end app."); 
     Console.ReadLine(); 
    } 
    static void Listen() 
    { 
     try 
     { 
      Console.WriteLine("Starting to listen"); 
      TcpListener listener = new TcpListener(IPAddress.Any, 4070); 
      listener.Start(); 
      Socket socket = listener.AcceptSocket(); 
      Console.WriteLine("Connected!"); 
      return; 
     } 
     catch (ThreadAbortException exception) 
     { 
      Console.WriteLine("Abort requested"); 
     } 
    } 
} 

La llamada thread.Abort() debe detener el AcceptSocket y ejecutar el manejador ThreadAbortException. Howerver esto no sucede.

Intercambio de mi Escuchar envoltorio de AcceptSocket para ListenAsync que exige BeginAcceptSocket lugar:

static void ListenAsync() 
    { 
     try 
     { 
      ManualResetEvent clientConnected = new ManualResetEvent(false); 

      Console.WriteLine("Starting to listen"); 
      TcpListener listener = new TcpListener(IPAddress.Any, 4070); 
      listener.Start(); 
      clientConnected.Reset(); 
      var iasyncResult = listener.BeginAcceptSocket((ar) => 
      { 
       Socket socket = listener.EndAcceptSocket(ar); 
       Console.WriteLine("Connected!"); 
       clientConnected.Set(); 
      }, null); 
      clientConnected.WaitOne(); 

      return; 
     } 
     catch (ThreadAbortException exception) 
     { 
      Console.WriteLine("Abort requested"); 
     } 
    } 

En este caso "parece" que funcione bien. ThreadAbortException se detecta mientras el hilo está haciendo un WaitOne. Sin embargo, el hilo que fue creado por la llamada a BeginAcceptSocket todavía se está ejecutando y capaz de aceptar una toma (I verificado esto mediante la apertura de dicho puerto con Telnet)

Finalmente, añadí Listener.Stop como parte del controlador de TheadAbortException, y una oportunidad atrapar alrededor de la llamada EndAcceptSocket (ya que el socket está dispuesto por el Stop)

¿Es este realmente el mejor enfoque para iniciar y detener el proceso de escuchar conexiones de socket?

+0

¿Qué sucede con el evento clientConnected cuando mata el hilo? ¿Por qué no te deshaces de los objetos? ¿Qué haces con el socket una vez aceptado? –

+0

El uso de 'Thread.Abort' es casi siempre un hack sucio y debe evitarse. Deje que sus hilos terminen con elegancia ... no tire de la alfombra debajo de ellos. – spender

Respuesta

7

La razón por la cual Thread.Abort no aborta un hilo que está escuchando en un socket es porque ese hilo está bloqueado en el interior (el equivalente moral de) una llamada al kernel listen(). El CLR solo puede generar el ThreadAbortException cuando el CLR se está ejecutando realmente, y durante una llamada al hilo listen está realmente atorado dentro de una llamada al sistema no administrada. Una vez que la llamada a listen retorna y la ejecución se reanuda dentro del CLR, el aborto del hilo puede continuarse.

En segundo lugar, aconsejo no utilizar Thread.Abort(). Si decide ir por esa ruta, puede usar una técnica similar a la de su segundo ejemplo. Sin embargo, sería mejor utilizar un método diferente y simplemente llamar al Listener.Stop() cuando desee apagar el oyente de subprocesos.

+1

No estoy seguro de qué pasa con Thead.Abort(); sin embargo, me complace escuchar los consejos e investigar otras opciones. –

+3

@Ralph: desde una perspectiva amplia, 'Thread.Abort' tiene problemas porque: a) (como se ve aquí), da la falsa percepción de que el hilo objetivo se cancelará inmediatamente (o incluso * pronto *), pero el CLR está a merced del código no administrado, yb) es destructivo y no se sabe en qué estado estará el hilo cuando se interrumpe. Se recomienda encarecidamente que en su lugar utilice una bandera de cancelación/cancelación de algún tipo y use métodos de bloqueo con tiempos de espera cuando sea posible. De esta forma puede verificar su bandera de cancelación y hacer cualquier limpieza (si corresponde) que sea apropiada en ese momento. –

Cuestiones relacionadas