2010-06-17 21 views
6

Digamos que tengo un Trabajador de la siguiente manera:Cómo iniciar y detener un Continuamente Correr Trabajador de fondo mediante un botón de

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
     { 
      while(true) 
      { 
        //Kill zombies 
      } 
     } 

¿Cómo puedo hacer que este inicio trabajador de fondo y dejar de usar un botón en un WinForm?

+3

Hmm, por la respuesta que recibieron, ten cuidado - usted se está preocupando todo acerca de un efecto que sólo hará que no dejar de una iteración o dos más tarde, ¡pero olvidando que vas a estar OCUPANDO todo un procesador simplemente girando! Al menos pon un sueño allí. Parecerá que está funcionando pero en gran detrimento.Algunas de las otras respuestas resuelven todos esos problemas de la forma en que 'se supone' resolverlos. – FastAl

Respuesta

5
+0

Funciona bien: D – sooprise

+0

"Acceder y actualizar un bool es una operación atómica". ¿Esto es debido a la barrera de la memoria? –

+0

No, atómico significa que el valor verdadero o falso se escribirá de una vez. Una barrera de memoria dicta el orden que escribe o lee el valor que ve otro hilo. No hay nada que diga que el valor escrito en _performKilling sea visible en otro hilo sin dicho mecanismo. La atomicidad significa que el valor leído siempre será verdadero o falso, no significa que el hilo de fondo verá el valor escrito más reciente. –

0

no he probado esto, tengo código en algún lugar que voy a tener que ver exactamente lo que hice, pero algo como esto es una adaptación de la respuesta de Fredrik:

private bool _performKilling; 
private object _lockObject = new object(); 

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    while(true) 
    { 

     if (_performKilling) 
     { 
      //Kill zombies 
     } 
     else 
     { //We pause until we are woken up so as not to consume cycles 
      Monitor.Wait(_lockObject); 
     } 
    } 
} 

private void StartKilling() 
{ 
    _performKilling = true; 
    Monitor.Pulse(_lockObject); 
} 

private void StopAllThatKilling() 
{ 
    _performKilling = false; 
] 

ejemplo más completo de este patrón aquí:

https://github.com/AaronLS/CellularAutomataAsNeuralNetwork/blob/fe9e6b950e5e28d2c99350cb8ff3157720555e14/CellLifeGame1/Modeling.cs

+0

Ah, por supuesto; buena solución. Debo estar cansado de no pensarlo desde el principio;) –

+0

_ "No lo he probado" _ - claramente, ya que las llamadas a 'Wait()' y 'Pulse()' arrojarán excepciones, porque no ha tomado el monitor antes de llamar a esos métodos. –

+0

@PeterDuniho Por supuesto, no se muestran plomería aquí. Se suponía que debía utilizar los dos métodos que uno usa en un monitor para facilitar la pausa/unpausing. El ejemplo más completo aquí: https://github.com/AaronLS/CellularAutomataAsNeuralNetwork/blob/fe9e6b950e5e28d2c99350cb8ff3157720555e14/CellLifeGame1/Modeling.cs – AaronLS

17

Tal vez usted puede utilizar un ManualResetEvent como este, me di No depuré esto, pero vale la pena intentarlo. Si funciona no va a tener el hilo girar sus ruedas mientras se está a la espera

ManualResetEvent run = new ManualResetEvent(true); 

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    while(run.WaitOne()) 
    { 
     //Kill zombies 
    } 
} 

private void War() 
{ 
    run.Set(); 
} 

private void Peace() 
{ 
    run.Reset(); 
} 
+0

+1 Es genial ver a otra alma que realmente entiende .NET threading. ;) Creo que el uso de un WaitHandle siempre es la mejor opción en estas situaciones, ya que intrínsecamente se ocupan de la atomicidad y otros problemas de subprocesamiento múltiple para usted (suponiendo que elija el tipo correcto de WaitHandle). – jrista

+0

'while (true) {if (run.WaitOne()) {}}'? Parece un poco redundante, ¿no? ¿Por qué no 'while (run.WaitOne()) {}'? –

+0

De hecho, pensé que después de publicarlo, lo actualizaré –

7

Utilice el método CancelAsync.

backgroundworker1.CancelAsync(); 

En su bucle dentro del hilo de trabajo.

if (backgroundWorker.CancellationPending) return; 

Esto no ocurre inmediatamente.

+5

+1: No entiendo por qué todos dicen que marque 'while (_someCustomBoolean)' cuando la clase 'BackgroundWorker' ya tiene esta funcionalidad incorporada. –

+1

Una vez que se establece la cancelación pendiente, creo que el protocolo general es para que el trabajador en segundo plano se detenga y complete. En esta situación, creo que las preguntas estaban pidiendo una manera de comenzar y detenerse. El asistente social también puede hacerte tropezar. Si utilizas su evento incompleto, esto se publica a través del ThreadPool genérico .net, he visto este punto muerto antes de depender de qué más se lanza allí. –

4

Por parada, ¿realmente quieres decir detener o quieres decir pausa? Si quieres parar, entonces esto es pan comido. Cree un controlador de evento de clic de botón para el botón que desea que sea responsable de iniciar el trabajador de fondo y un controlador de evento de clic de botón para el responsable de detenerlo. En su botón de inicio, realice una llamada al método de trabajador en segundo plano que activa el evento do_work. Algo como esto:

private void startButton_Click(System.Object sender, 
    System.EventArgs e) 
{ 
    // Start the asynchronous operation. 
    backgroundWorker1.RunWorkerAsync(); 
} 

En el botón de parada, hacer una llamada al método que establece de CancellationPending a verdadero trabajador de fondo, como esto:

private void cancelAsyncButton_Click(System.Object sender, 
    System.EventArgs e) 
{ 
    // Cancel the asynchronous operation. 
    this.backgroundWorker1.CancelAsync(); 
} 

Ahora no se olvide de comprobar la CancelationPending bandera dentro de su doWork del trabajador de fondo. Algo como esto:

private void KillZombies(BackgroundWorker worker, DoWorkEventArgs e) 
    { 
     while (true) 
     { 
      if (worker.CancellationPending) 
      { 
       e.Cancel = true; 
      } 
     } 
    } 

Y su método de DoWork:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
     { 
      BackgroundWorker worker = sender as BackgroundWorker; 
      KillZombies(worker, e); 
     } 

espero que esto puede guiarlo en la dirección correcta. Algunas lecturas adicionales:

http://msdn.microsoft.com/en-us/library/b2zk6580(v=VS.90).aspx

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

http://msdn.microsoft.com/en-us/library/waw3xexc.aspx

Cuestiones relacionadas