2012-05-06 204 views
12

¿Cómo puedo pausar/reanudar un hilo? Una vez que Join() un hilo, no puedo reiniciarlo. Entonces, ¿cómo puedo iniciar un hilo y hacer que se detenga cada vez que se presiona el botón 'pausa', y reanudarlo cuando se presiona el botón reanudar?cómo pausar/reanudar un hilo

Lo único que hace este hilo, es mostrar texto al azar en un control de etiqueta.

+1

Puede agregar el código en su pregunta – Adil

+0

No hay ningún código por el momento. Todavía estoy pensando cómo implementar este proyecto que tengo en mi cabeza. Todo lo que puedo decir es que sé por experiencia con mi trabajo de enhebrado, una vez que un hilo se une con el hilo principal, no hay forma de que pueda reanudarlo/reiniciarlo. – Yustme

Respuesta

16

Tal vez el ManualResetEvent es una buena opción. Un ejemplo corto:

private static EventWaitHandle waitHandle = new ManualResetEvent(initialState: true); 

// Main thread 
public void OnPauseClick(...) { 
    waitHandle.Reset(); 
} 

public void OnResumeClick(...) { 
    waitHandle.Set(); 
} 

// Worker thread 
public void DoSth() { 
    while (true) { 
    // show some random text in a label control (btw. you have to 
    // dispatch the action onto the main thread) 
    waitHandle.WaitOne(); // waits for the signal to be set 
    } 
} 
+0

No funciona en VirtualBox XP. ¿No sabe por qué? – zionpi

+0

Estuve tentado de declinar el uso de [_reserved keyword_] (https://msdn.microsoft.com/en-us/library/x53a06bb.aspx) como un nombre de variable, pero la solución funcionó demasiado bien para mí para troll :-) – MDMoore313

+0

No estoy definiendo eventos (♡ Rx ♡), así que no sabía que 'event' es una palabra clave :-) –

5

que podría sugerir a leer Threading in C#, by Joe Albahari, particularmente Suspend and Resume sección:

Un hilo puede ser suspendido de forma explícita y se reanuda a través de los métodos obsoletos Thread.suspend y Thread.Resume. Este mecanismo está completamente separado del de bloqueo. Ambos sistemas son independientes y operan en paralelo.

Un hilo puede suspenderse a sí mismo u otro hilo. Llamar a Suspender da como resultado que el hilo ingrese brevemente en el estado SuspendRequested y luego, al llegar a un punto seguro para la recolección de elementos no utilizados, ingrese al estado Suspendido. A partir de ahí, solo se puede reanudar a través de otro hilo que llame a su método Resume. El currículum funcionará solo en un hilo suspendido, no en un hilo bloqueado.

De .NET 2.0, Suspender y Reanudar han sido desaprobados, su uso desaconsejado debido al peligro inherente a la suspensión arbitraria de otro hilo. Si se suspende un hilo que mantiene un bloqueo en un recurso crítico, toda la aplicación (o computadora) puede estancarse. Esto es mucho más peligroso que llamar a Abort, lo que da como resultado la liberación de dichos bloqueos (al menos teóricamente) en virtud del código en los bloques finally.

SuspendRequested state

+0

Su comillas de bloque hace una distinción entre un hilo suspendido versus un hilo bloqueado, pero el diagrama no parece hacer esta distinción. Es esto intencional? – Tung

+0

@gliderkite, ver mi respuesta a Adil sobre mostrar el código. – Yustme

+0

@Tung Un subproceso se considera bloqueado cuando su ejecución se detiene por algún motivo, como cuando se está Dormido o esperando que otro finalice a través de Join o EndInvoke. En el código: 'bool blocked = (someThread.ThreadState & ThreadState.WaitSleepJoin)! = 0;'. Entonces, ¿por qué no hay distinción en el diagrama? Ver [esto] (http://www.albahari.com/threading/part2.aspx#_Blocking). – gliderkite

2

No es la mejor idea para suspender y reanudar las discusiones manualmente. Sin embargo, puede simular fácilmente este comportamiento utilizando primitivas de sincronización de subprocesos (como ManualResetEvent)

Eche un vistazo a this question, puede que le sea útil.

Pero creo que puede alcanzar fácilmente su objetivo de "mostrar texto aleatorio en un control de etiqueta" por tiempo mediante el uso de temporizadores.

Aquí está un ejemplo rápido usando DispatcherTimer

var timer = new DispatcherTimer(); 
timer.Tick += (s, e) => Label.Text = GetRandomText(); 
timer.Interval = TimeSpan.FromMilliseconds(500); 
timer.Start(); 

Puede hacer una pausa llamando timer.Stop() y luego timer.Start() nuevo para continuar.

+0

Hola, esto se ve bien, ¿puedo poner este código en un hilo separado también? ? Porque no quiero bloquear el hilo de la GUI. – Yustme

+0

@Yustme: AFAIK el código de 'DispatcherTimer' se ejecuta en el hilo de la GUI. Si desea un hilo separado, use 'System.Threading.Timer'. – Tudor

+0

Tudor tiene razón, por lo que si tiene algún código "pesado" que deba ejecutarse en los ticks del temporizador, use un hilo por separado. Pero si solo necesita actualizar el bloque de texto, usar 'DispatcherTimer' está bien y no bloqueará su UI. –

1

Aquí hay dos maneras en que funcionó para mí. Ambos suponen que el hilo de trabajo tiene su propio ciclo de procesamiento.

  1. Tener el hilo invocar una devolución de llamada para solicitar permiso para seguir adelante
  2. que el padre invocar un método en la clase del hilo para señalar que

El ejemplo de aplicación de consola siguiente muestra ambos enfoques, utilizando una devolución de llamada para pausar/continuar, y un método de trabajador para detenerse.Otra ventaja del método de devolución de llamada es que también es conveniente para reenviar las actualizaciones de estado mientras se comprueba la autorización para continuar.

using System; 
using System.Threading; 

namespace ConsoleApplication7 
{ 
    class Program 
    { 
     static bool keepGoing; 
     static void Main(string[] args) 
     { 
      keepGoing = true; 
      Worker worker = new Worker(new KeepGoingDelegate(KeepGoing)); 
      Thread thread = new Thread(worker.DoWork); 
      thread.IsBackground = true; 
      thread.Start(); 

      while (thread.ThreadState != ThreadState.Stopped) 
      { 
       switch (Console.ReadKey(true).KeyChar) 
       { 
        case 'p': 
         keepGoing = false; 
         break; 
        case 'w': 
         keepGoing = true; 
         break; 
        case 's': 
         worker.Stop(); 
         break; 
       } 
       Thread.Sleep(100); 
      } 
      Console.WriteLine("Done"); 
      Console.ReadKey(); 
     } 

     static bool KeepGoing() 
     { 
      return keepGoing; 
     } 
    } 

    public delegate bool KeepGoingDelegate(); 
    public class Worker 
    { 
     bool stop = false; 
     KeepGoingDelegate KeepGoingCallback; 
     public Worker(KeepGoingDelegate callbackArg) 
     { 
      KeepGoingCallback = callbackArg; 
     } 

     public void DoWork() 
     { 
      while (!stop) 
      { 
       Console.Write(KeepGoingCallback()?"\rWorking":"\rPaused "); 

       Thread.Sleep(100); 
      } 
      Console.WriteLine("\nStopped"); 
     } 

     public void Stop() 
     { 
      stop = true; 
     } 
    } 
} 
+0

Hola, por mi vida estoy teniendo problemas para entender a los delegados. Puedo averiguar cómo detener el hilo, pero no entiendo cómo lo está reiniciando. – Zev

+0

@Zev: un delegado es simplemente un marcador de posición para un método, de modo que puede pasarlo a una instancia de otra clase para que pueda ejecutarlo. En mi ejemplo, el padre transfiere el método 'KeepGoing()' al trabajador, que periódicamente llama para consultar con el padre para ver si debe continuar. El subproceso de trabajo no se reinicia cada vez: se inicia una vez y continúa hasta que recibe una señal de detención. –