2010-11-15 20 views
5

En este código, cuando se hace clic dos veces en button1, se crean 2 hilos separados. Con un clic, crea un nuevo hilo en el montón y el campo t1 apunta al nuevo hilo en el montón. Cuando hago clic en button2, aborta el último hilo (al que se refiere t1).¿Cómo aborto múltiples hilos?

¿Cómo aborto el otro hilo?

Thread t1; 
ThreadStart ts1; 

private void button1_Click(object sender, EventArgs e) 
{ 
    ts1 = new ThreadStart(myfunc); 
    t1 = new Thread(ts1); 
    t1.Start(); 
} 

private void button2_Click(object sender, EventArgs e) 
{ 
    t1.Abort(); 
} 
+0

¿Hay alguna razón por la que no pueda usar BackgroundWorker o una construcción similar? Los hilos de desove implican muchos gastos generales, por lo que tiene sentido reutilizarlos siempre que sea posible. –

Respuesta

7

Bueno, la respuesta OO sería mantener una lista de hilos como un campo.

private readonly List<Thread> threads = new List<Thread>(); 

Y para luego agregar el hilo recién construido a la lista en el primer controlador.

var thread = new Thread(myfunc); 
thread.Start(); 
threads.Add(thread); 

Luego puede repetir cada hilo en el segundo controlador, abortando cada uno de ellos sucesivamente.

foreach(var thread in threads) 
    thread.Abort(); 

pero creo que el punto más importante aquí es que no es casi nunca una buena razón para llamar Thread.Abort.

Desde el MSDN page:

Cuando llama un hilo Abortar sobre sí misma, el efecto es similar a lanzar una excepción ; la excepción ThreadAbortException ocurre inmediatamente y el resultado es predecible. Sin embargo, si un hilo llama Abort en otro hilo, el aborta interrumpe el código que se está ejecutando. También existe la posibilidad de que un constructor estático se pueda cancelar. En casos excepcionales, esto podría evitar que instancias de esa clase sean creadas en ese dominio de aplicación. En .NET Framework versiones 1.0 y 1.1, existe la posibilidad de que el hilo pueda abortar mientras se ejecuta finalmente un bloque , en cuyo caso se cancela el bloque finally .

El subproceso que llama Abort podría bloque si el hilo que está siendo abortado está en una región protegida de código, tal como un bloque de captura, finalmente bloque, o constreñido región ejecución . Si el hilo que llama Abort contiene un bloqueo que requiere el hilo abortado , puede producirse un interbloqueo.

que sería mucho mejor usar algún tipo de señalización, tales como el establecimiento de un ManualResetEvent que cada hilo sondeará a intervalos périodicas. Alternativamente, puede usar la clase BackgroundWorker que tiene algún soporte para la cancelación de tarea (llame al CancelAsync y obtenga los hilos de trabajo para probar CancellationPending periódicamente). Si está en .NET 4.0, también puede usar el TPL.

+0

Buena descripción. Pero creo que "foreach (var thread in threads)" puede causar Exception, "lock" debe usarse aquí. – acoolaum

+0

@acoolaum: Gracias. Por lo que puedo decir, un 'bloqueo' no es necesario aquí. Ambos manejadores se ejecutan en el bucle de mensaje del formulario; no pueden ejecutarse simultáneamente en diferentes hilos. – Ani

+0

sí, tienes razón. – acoolaum

1

yo le recomendaría a echar un vistazo a la incorporada en las primitivas de sincronización como ManualResetEvent y WaitHandle. Puede preguntar un hilo si se está ejecutando o no tratando de unir el hilo con Thread.Join. Abortar un hilo solo debe hacerse como último recurso si el hilo no responde.

Aquí hay un ejemplo modificado de su código que muestra cómo puede evitar que el hilo se reinicie antes de que se haya detenido correctamente.

public partial class MainForm : Form 
{ 
    private Thread t1; 
    private ThreadStart ts1; 
    private ManualResetEvent t1resetEvent; 

    public MainForm() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     // Got a thread? 
     if (t1 != null) {     
      if (!t1.Join(0)) { 
       // The thread seems to be running. 
       // You have to stop the thread first. 
       return; 
      } 
     } 

     t1resetEvent = new ManualResetEvent(false); 
     ts1 = new ThreadStart(MyFunc); 
     t1 = new Thread(ts1); 
     t1.Start(); 
    } 

    private void button2_Click(object sender, EventArgs e) 
    { 
     // Got a thread? 
     if (t1 != null) 
     { 
      // Set the reset event so the thread 
      // knows it's time to stop. 
      t1resetEvent.Set(); 

      // Give the thread four seconds to stop. 
      if (!t1.Join(4000)) { 
       // It did not stop, so abort it. 
       t1.Abort(); 
      } 
     } 
    } 

    private void MyFunc() 
    { 
     // Long running operation... 
     while (true) 
     { 
      // Do someone want us to exit? 
      if (t1resetEvent.WaitOne(0)) { 
       return; 
      }     
     } 
    } 
} 
+0

Un problema con eso es que bloqueará la interfaz de usuario hasta que pueda unirse a t1 –

1

Los otros han dado las versiones largas de la respuesta, sin embargo, la simple solución obvia es saltarse simplemente recrear el objeto hilo:

public partial class Form1 : Form 
{ 
    Thread thread1; 
    ThreadStart threadStart1; 

    public Form1() 
    { 
     InitializeComponent(); 

     threadStart1 = new ThreadStart(threadTarget); 
     thread1 = new Thread(threadStart1); 
     thread1.Name = "Button1 thread";  
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     thread1.Start(); 
    } 

    private void button2_Click(object sender, EventArgs e) 
    { 
     thread1.Abort(); 
    } 

    private void threadTarget() 
    { 
     Console.WriteLine(Thread.CurrentThread.Name); 
     for (int i = 0; i < 100; i++) 
     { 
      Console.WriteLine(i); 
      Thread.Sleep(500); 
     } 

    } 
} 

Sin embargo, consideraría leer sobre roscar en. NET using one these guides (Recomendaría Joseph Albahari's guide on aborting - el autor de C# en pocas palabras) en lugar de utilizar este método, especialmente si está realizando operaciones IO o de bases de datos que pueden dejar los objetos en estados inesperados.

Cuestiones relacionadas