2011-10-20 17 views
5

En una aplicación que estoy desarrollando, tengo un formulario principal que simplemente se sienta allí y muestra datos de registro, y un hilo de trabajo que realiza el trabajo de forma autónoma en un bucle.Cierre con gracia un hilo

MyWorker worker = new MyWorker(); 
MainForm mainForm = new MainForm(); 

// Subscribe form to log event so log data gets displayed 
worker.Log += mainForm.Log; 

// Start the worker thread's MainLoop 
new Thread(new ThreadStart(worker.MainLoop)).Start(); 

// Show the form (blocking) 
Application.Run(mainForm); 

// If we end up here, the form has been closed and the worker has to stop running    
worker.Running = false; 

Como puede ver, cada vez que se cierra el formulario, el hilo de trabajo debe detenerse. El trabajador tiene el siguiente aspecto:

public class MyWorker 
{ 
    public String Running { get; set; } 

    public MyWorker() 
    { 
     Running = true; 
    } 

    public void MainLoop() 
    { 

     while (Running) 
     { 

      DoExtensiveWork1(); 
      if (!Running) return; 

      DoExtensiveWork2(); 
      if (!Running) return; 

      DoExtensiveWork3(); 
      if (!Running) return; 

      DoExtensiveWork4(); 
      if (!Running) return; 

      DoExtensiveWork5();   
      if (!Running) return; 

      // We have to wait fifteen minutes (900 seconds) 
      // before another "run" can be processed 
      for (int i = 0; i < 900; i++) 
      { 
       Thread.Sleep(1000); 
       if (!Running) return; 
      } 
     } 
    } 
} 

Como se puede ver, quiero que el hilo sea capaz de parar cuando se cambia entre sucesivas operaciones de trabajo, pero no cuando dentro de una operación. Cuando finaliza una operación (DoExtensiveWorkN), su estado y resultados se mantienen en el disco o la base de datos, por lo que salir mientras una operación está en progreso (por ejemplo, Thread.Abort) no es una opción.

Sin embargo, creo que este código que acabo de escribir es repulsivo, especialmente el "bucle de espera" que duerme 900 segundos para evitar que el hilo funcione durante 15 minutos antes de detectar Running se ha configurado para false.

Prefiero ser capaz de lanzar algún tipo de evento para detener el ciclo principal tan pronto como haya terminado un trabajo.

¿Alguien puede indicarme en la dirección correcta cómo hacer esto, o si se requiere una reescritura total porque entiendo completamente el enhebrado, muéstreme en algún lugar donde se explican esos principios?

Respuesta

13

Puede organizar considerablemente tanto el funcionamiento de las tareas individuales como el ciclo de espera de 15 minutos.

me gustaría sugerir tal vez usando algo como esto:

public class MyWorker 
{ 
    private readonly ManualResetEvent _stopEvent = new ManualResetEvent(false); 
    private readonly Action[] _workUnits; 

    private bool Running 
    { 
     get { return !_stopEvent.WaitOne(0); } 
    } 

    public MyWorker() 
    { 
     _workUnits = new Action[] 
     { 
      DoExtensiveWork1, 
      DoExtensiveWork2, 
      DoExtensiveWork3, 
      DoExtensiveWork4, 
      DoExtensiveWork5 
     }; 
    } 

    public void Stop() 
    { 
     _stopEvent.Set(); 
    } 

    public void MainLoop() 
    { 

     while (Running) 
     { 
      foreach (var workUnit in _workUnits) 
      { 
       workUnit(); 
       if (!Running) return; 
      }   

      // We have to wait fifteen minutes (900 seconds) 
      // before another "run" can be processed 
      if (_stopEvent.WaitOne(900000)) return; 
     } 
    } 
} 

Luego de detener el proceso en el siguiente punto apropiado:

Worker.Stop(); 
0

se recomienda usar System.Timers.Timer.

Puede hacer su trabajo con la ejecución y, en lugar de utilizar el modo de suspensión, puede configurar el temporizador para que se apague de nuevo en 15 minutos.

Si desea detenerlo antes de tiempo, llame a algún tipo de método de cancelación (similar a la configuración de la variable Running = true) que detendrá el temporizador.

Cabe señalar que cada vez que se dispara el evento del temporizador se iniciará un nuevo hilo por lo que no tiene que preocuparse por matar a los hilos de fondo. Su hilo termina su procesamiento, configura el temporizador para que se ejecute en 15 minutos y luego el hilo termina naturalmente. Si abortas durante una espera, simplemente te deshaces del temporizador y no necesitas más limpieza. Si abortas durante una ejecución, dejas que termine la ejecución y al final verifica una bandera y no vuelve a iniciar el temporizador y luego termina la secuencia.

Para el temporizador, querrá configurar el temporizador para que se inicie manualmente al final del proceso. La alternativa es hacer que el temporizador marque cada 15 minutos, pero eso significaría que si su procesamiento tomó 10 minutos, entonces solo tardaría 5 minutos antes de la próxima ejecución. Y si tomó más de 15 minutos puede estar en problemas. Además, al reiniciar manualmente el temporizador se garantiza que el proceso no se reinicie mientras se está ejecutando otro.

+0

Los temporizadores no resolverán el problema principal que tengo: no quiero que el trabajo continúe cuando se cierre el formulario, y no quiero agregar mucho código para seguir buscando eso. – CodeCaster

+0

@CodeCaster: Ah, malentendí "por lo que dejar de fumar mientras una operación está en progreso (por ejemplo, Thread.Abort) no es una opción" para significar todo el hilo. Mi pensamiento entonces sería usar una variedad de Acciones pero Iridium me ganó. Todavía prefiero el temporizador para la espera de 15 minutos. :) – Chris

+0

Podría haberlo descrito más claramente. Gracias por la sugerencia, sin embargo. :-) – CodeCaster

Cuestiones relacionadas