2009-04-29 14 views
47

Estoy escribiendo una aplicación de Windows que ejecuta una secuencia de acciones digitales de E/S repetidamente.¿Cómo "matar" al trabajador de fondo por completo?

Esta secuencia de acciones se inicia cuando el usuario haga clic en un botón de "START", y que es realizado por un trabajador de fondo en backgroundWorker1_DoWork().

Sin embargo, hay ocasiones en que me sale el "Este BackgroundWorker está actualmente ocupado ......." mensaje de error.

Estoy pensando en implementar lo siguiente en el código, mediante el uso de un bucle while para "matar" el trabajador fondo antes de iniciar otra secuencia de acción:

if (backgroundWorker1.IsBusy == true) 
{ 

    backgroundWorker1.CancelAsync(); 
    while (backgroundWorker1.IsBusy == true) 
    { 
     backgroundWorker1.CancelAsync(); 
    } 

    backgroundWorker1.Dispose(); 

} 

backgroundWorker1.RunWorkerAsync(); 

Creo que mi principal preocupación es, será el backgroundWorker1 ser "asesinado" eventualmente? Si lo hace, ¿llevará mucho tiempo completarlo?

¿Esta codificación me llevará a un ciclo infinito?

+2

¿Por qué necesita un bucle? ¿No lo mataría una vez significa que ya no está ocupado? – Louis

+0

Además, sería aconsejable dormir allí que sea apropiado para la aplicación. Además, ¿qué sucede si el trabajador está legítimamente ocupado? ¿Seguirías queriendo matarlo en medio de un proceso? Creo que sería más prudente detener lo que siempre está forzando al empleado de fondo a ocupar indefinidamente. – Louis

Respuesta

16

Soy de la opinión de que los hilos deben ser responsables de sus propios recursos tanto como sea posible, incluyendo su propia vida útil.

Por lo general es una mala idea para matar hilos desde fuera de su alcance. Las aplicaciones que están diseñadas para pasar un mensaje al hilo para cerrar en sí tienden a tener menos problemas relacionados con el comportamiento de subprocesos múltiples.

Un subproceso debe supervisar dicho mensaje, que puede ser tan simple como un booleano establecido por otro subproceso y leído por ese subproceso de supervisión, en el momento oportuno y cerrarse limpiamente tan pronto como sea posible.

Eso significa que si se debe buscar el mensaje:

  • en su bucle principal, en su caso.
  • periódicamente en cualquier bucle de larga ejecución.

El hilo que se cierra con el mensaje debe esperar (pero no detener la GUI, por supuesto).

Tenga en cuenta que hay otras posibilidades para entornos roscados con capacidades específicas, tales como el caso en que las discusiones pueden marcarse revocable a voluntad, para permitir la matanza externa sea más seguro.

Pero sigue siendo por lo general más fácil simplemente arquitecto su aplicación para dejar un maestro hilo de su propio destino.

+2

Si el subproceso accede a un recurso externo que podría bloquear el subproceso, entonces matar al subproceso podría ser la única opción – HamoriZ

+0

De ahí la frase: "tanto como sea posible" :-) – paxdiablo

-1

que estaba teniendo el mismo problema, no estoy seguro de si esto ayuda pero supongo que su trabajador de fondo tiene un bucle dentro o sería salir. Lo que tienes que hacer es poner tu lazo dentro.

poner dentro de su trabajador de fondo:

while (backgroundworker1.CancellationPending == false) 
{ 
    //Put your code in here 
} 

para matar a este BackgroundWorker, se puede poner en su botón:

BackgroundWorker1.CancelAsync() 

espero que esto ayude.

+3

¡Limpie su formato e inglés! –

+1

Válida respuesta pero formato terrible ... – John

+0

Por favor, aprenda a hablar. – ro0ter

69

Puede usar algo como esto (para más información acerca de abortar subprocesos administrados y sobre ThreadAbortException ver "Plumbing the Depths of the ThreadAbortException Using Rotor" por Chris Sells):

public class AbortableBackgroundWorker : BackgroundWorker 
{ 

    private Thread workerThread; 

    protected override void OnDoWork(DoWorkEventArgs e) 
    { 
     workerThread = Thread.CurrentThread; 
     try 
     { 
      base.OnDoWork(e); 
     } 
     catch (ThreadAbortException) 
     { 
      e.Cancel = true; //We must set Cancel property to true! 
      Thread.ResetAbort(); //Prevents ThreadAbortException propagation 
     } 
    } 


    public void Abort() 
    { 
     if (workerThread != null) 
     { 
      workerThread.Abort(); 
      workerThread = null; 
     } 
    } 
} 

Uso:

backgroundWorker1 = new AbortableBackgroundWorker(); 
//... 
backgroundWorker1.RunWorkerAsync(); 

if (backgroundWorker1.IsBusy == true) 
{ 
    backgroundWorker1.Abort(); 
    backgroundWorker1.Dispose(); 
} 
+0

Me gusta mucho esto. Realmente parece hacer el trabajo. Tengo algunas cosas complicadas en el BackgroundWorker, pero no se escribe nada hasta que se complete. –

+3

Por cierto, todavía es peligroso matar el hilo con Thread.Abort, porque no todas las clases en .NET Framework son robustas en términos de excepciones asincrónicas. Pero si sabes lo que estás haciendo, está bien. –

+0

Estaba muy contento de encontrar este código al principio, pero cuando lo intenté Le llevó años abortar el hilo ... No esperaba tal comportamiento y estoy un poco confundido. Realmente no tengo idea de por qué esto lleva tanto tiempo –

0

que juntar uno eso (creo) hace el trabajo. Por favor, avíseme si estoy fuera de combate. Aquí hay un simple ejemplo de cómo funciona.

var backgroundWorker = new BackgroundWorker(){WorkerSupportsCancellation = true}; 

backgroundWorker.DoWork += (sender, args) => 
     {     
       var thisWorker = sender as BackgroundWorker; 
       var _child = new Thread(() => 
               { 
                //..Do Some Code 

               }); 
       _child .Start(); 
       while (_child.IsAlive) 
       { 
        if (thisWorker.CancellationPending) 
        { 
         _child.Abort(); 
         args.Cancel = true; 
        } 
        Thread.SpinWait(1); 
       }     
     }; 

backgroundWorker.RunWorkerAsync(parameter); 
//..Do Something... 
backgroundWorker.CancelAsync(); 

Dado que el trabajador en segundo plano es parte del grupo de subprocesos, no queremos abortarlo. Pero podemos ejecutar un hilo interno que podemos permitir que ocurra un aborto. El backgroundWorker básicamente se ejecuta hasta que el hilo hijo esté completo o le indicamos que mate el proceso. El hilo del trabajador de fondo puede regresar al grupo de lectura. Normalmente voy a resumir esto en una clase de ayuda y paso por el método de delegado que quiero que el subproceso en segundo plano se transfiera como parámetro y lo ejecute en el subproceso secundario.

Por favor, alguien dígame si estoy golpeando mi cabeza contra la pared, pero parece que funciona bien ... Pero ese es el problema con los hilos, ¿no es así ... los diversos resultados que puede obtener cuando lo ejecuta en momentos diferentes.

+0

-1: para '.Abort()' en una nueva sugerencia de diseño. –

+2

Muy, muy peligroso "abortar" un hilo externamente. Por favor, lea: http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation. Básicamente, esto está muy mal en ** tres ** formas: (1) creando un hilo adicional innecesario, (2) ocupado-esperando en él, y (3) abortándolo en un momento potencialmente crítico sin permitir una limpieza adecuada. – Aaronaught

+0

La respuesta anterior también usa un subproceso hijo + abortar tipo de solución. ¿Hay una mejor manera de lidiar con esto? – Rob

-1
public class abortableBackgroundworker: BackgroundWorker 
{ 
    public bool Kill = false; 

    protected override void OnDoWork(DoWorkEventArgs e) 
    { 


     var _child = new Thread(() => 
     { 
      while (!Kill) 
      { 

      } 
      e.Cancel = true;//..Do Some Code 

     }); 
     _child.Start(); 
     base.OnDoWork(e); 

    } 



} 

configura matar a cierto para matar el hilo y no hay problema de interrupción :)

+5

Dude este código mató a mi 24-Core Xeon xD –

Cuestiones relacionadas