¿Hay alguna forma de pausar indefinidamente un hilo?

He estado trabajando en una aplicación .NET de rastreo web en mi tiempo libre, y una de las características de esta aplicación que quería incluir fue un botón de pausa para detener una cadena específica.

Soy relativamente nuevo en multi-threading y no he podido encontrar la manera de detener un hilo indefinidamente que es compatible actualmente. No recuerdo la clase / método exacto, pero sé que hay una forma de hacerlo, pero el framework .NET lo ha marcado como obsoleto.

¿Hay alguna buena manera de propósito general para detener indefinidamente un hilo de trabajo en C # .NET.

Últimamente no he tenido mucho tiempo para trabajar en esta aplicación y la última vez que la toqué fue en el marco .NET 2.0. Estoy abierto a todas las características nuevas (si las hay) que existen en el marco .NET 3.5, pero me gustaría saber qué solución también funciona en el framework 2.0 ya que eso es lo que uso en el trabajo y sería bueno saber por si acaso.

Nunca, nunca use Thread.Suspend . El principal problema es que el 99% de las veces no puedes saber qué está haciendo ese hilo cuando lo suspendes. Si ese hilo tiene un locking, es más fácil entrar en una situación de punto muerto, etc. Tenga en cuenta que el código que está llamando puede estar adquiriendo / liberando lockings detrás de escena. Win32 tiene una API similar: SuspendThread y ResumeThread . Los siguientes documentos para SuspendThread ofrecen un buen resumen de los peligros de la API:

http://msdn.microsoft.com/en-us/library/ms686345(VS.85).aspx

Esta función está diseñada principalmente para su uso por depuradores. No está destinado a ser utilizado para la sincronización de hilos. Llamar a SuspendThread en un subproceso que posee un objeto de sincronización, como un mutex o sección crítica, puede llevar a un interlocking si el subproceso que llama intenta obtener un objeto de sincronización propiedad de un subproceso suspendido. Para evitar esta situación, un subproceso dentro de una aplicación que no es un depurador debe indicar al otro subproceso que se suspenda. El hilo de destino debe estar diseñado para vigilar esta señal y responder adecuadamente.

La forma correcta de suspender un hilo indefinidamente es usar un ManualResetEvent . Es muy probable que el hilo esté dando vueltas, realizando algún trabajo. La forma más fácil de suspender el hilo es hacer que el hilo “verifique” el evento en cada iteración, de esta manera:

 while (true) { _suspendEvent.WaitOne(Timeout.Infinite); // Do some work... } 

Especifica un tiempo de espera infinito para que cuando el evento no se señalice, el hilo se bloquee indefinidamente, hasta que se señale el evento, en cuyo punto el hilo se reanudará donde lo dejó.

Deberías crear el evento así:

 ManualResetEvent _suspendEvent = new ManualResetEvent(true); 

El parámetro true le dice al evento que comience en el estado señalado.

Cuando desee pausar el hilo, haga lo siguiente:

 _suspendEvent.Reset(); 

Y para resumir el hilo:

 _suspendEvent.Set(); 

Puede usar un mecanismo similar para señalar el hilo para salir y esperar en ambos eventos, detectando qué evento fue señalado.

Solo por diversión, daré un ejemplo completo:

 public class Worker { ManualResetEvent _shutdownEvent = new ManualResetEvent(false); ManualResetEvent _pauseEvent = new ManualResetEvent(true); Thread _thread; public Worker() { } public void Start() { _thread = new Thread(DoWork); _thread.Start(); } public void Pause() { _pauseEvent.Reset(); } public void Resume() { _pauseEvent.Set(); } public void Stop() { // Signal the shutdown event _shutdownEvent.Set(); // Make sure to resume any paused threads _pauseEvent.Set(); // Wait for the thread to exit _thread.Join(); } public void DoWork() { while (true) { _pauseEvent.WaitOne(Timeout.Infinite); if (_shutdownEvent.WaitOne(0)) break; // Do the work here.. } } } 

El libro electrónico Threading in C # resume Thread.Suspend y Thread.Resume así:

Los métodos de Suspender y Reanudar obsoletos tienen dos modos: ¡peligroso e inútil!

El libro recomienda utilizar una construcción de sincronización como AutoResetEvent o Monitor.Wait para realizar la suspensión y la reanudación del hilo.

Acabo de implementar una clase LoopingThread que LoopingThread una acción pasada al constructor. Está basado en la publicación de Brannon. He WaitForPause() otras cosas como WaitForPause() , WaitForStop() y una propiedad TimeBetween , que indica el tiempo que debe TimeBetween antes del próximo bucle.

También decidí cambiar el while-loop por un do-while-loop. Esto nos dará un comportamiento determinista para un Start() y Pause() sucesivos. Con determinista quiero decir, que la acción se ejecuta al menos una vez después de un comando de Start() . En la implementación de Brannon, este podría no ser el caso.

He omitido algunas cosas para la raíz del asunto. Cosas como “verificar si el hilo ya se inició”, o el patrón IDisposable .

 public class LoopingThread { private readonly Action _loopedAction; private readonly AutoResetEvent _pauseEvent; private readonly AutoResetEvent _resumeEvent; private readonly AutoResetEvent _stopEvent; private readonly AutoResetEvent _waitEvent; private readonly Thread _thread; public LoopingThread (Action loopedAction) { _loopedAction = loopedAction; _thread = new Thread (Loop); _pauseEvent = new AutoResetEvent (false); _resumeEvent = new AutoResetEvent (false); _stopEvent = new AutoResetEvent (false); _waitEvent = new AutoResetEvent (false); } public void Start () { _thread.Start(); } public void Pause (int timeout = 0) { _pauseEvent.Set(); _waitEvent.WaitOne (timeout); } public void Resume () { _resumeEvent.Set (); } public void Stop (int timeout = 0) { _stopEvent.Set(); _resumeEvent.Set(); _thread.Join (timeout); } public void WaitForPause () { Pause (Timeout.Infinite); } public void WaitForStop () { Stop (Timeout.Infinite); } public int PauseBetween { get; set; } private void Loop () { do { _loopedAction (); if (_pauseEvent.WaitOne (PauseBetween)) { _waitEvent.Set (); _resumeEvent.WaitOne (Timeout.Infinite); } } while (!_stopEvent.WaitOne (0)); } } 

Además de las sugerencias anteriores, me gustaría agregar un consejo. En algunos casos, use BackgroundWorker puede simplificar su código (especialmente cuando usa un método anónimo para definir DoWork y otros eventos de él).

De acuerdo con lo que dijeron los demás, no lo hagas. Lo que realmente quieres hacer es “pausar el trabajo” y dejar que tus hilos vaguen libremente. ¿Puede darnos más detalles sobre el (los) hilo (s) que desea suspender? Si no inició el hilo, definitivamente ni siquiera debería considerar suspenderlo, no es suyo. Si es su hilo, le sugiero que en vez de suspenderlo, simplemente lo tenga sentado, esperando que haga más trabajo. Brannon tiene algunas sugerencias excelentes para esta opción en su respuesta. Alternativamente, solo déjalo terminar; y gira uno nuevo cuando lo necesites.

Si no hay requisitos de sincronización:

Thread.Sleep(Timeout.Infinite);

Suspender () y Reanudar () pueden ser privados, sin embargo, de ninguna manera son inútiles. Si, por ejemplo, tiene un hilo que hace un trabajo largo que altera los datos, y el usuario desea detenerlo, hace clic en un botón. Por supuesto, debe solicitar la verificación, pero, al mismo tiempo, no desea que ese hilo continúe alterando los datos, si el usuario decide que realmente desea abortar. La suspensión del subproceso mientras se espera que el usuario haga clic en el botón Sí o No en el cuadro de diálogo de confirmación es la única manera de evitar que altere los datos, antes de señalar el evento de aborto designado que le permitirá detenerse. Los eventos pueden ser agradables para hilos simples que tienen un ciclo, pero los hilos complicados con procesamiento complejo es otro problema. Ciertamente, Suspender () nunca debe usarse para la sincronización, ya que su utilidad no es para esta función.

Solo es mi opinión.