BackgroundWorker vs hilo de fondo


166

Tengo una pregunta estilística sobre la elección de la implementación de subprocesos de fondo que debo usar en una aplicación de formulario de Windows. Actualmente tengo BackgroundWorkerun formulario que tiene un (while(true))bucle infinito . En este ciclo, uso WaitHandle.WaitAnypara mantener el hilo dormitando hasta que suceda algo interesante. Uno de los identificadores de eventos que espero es un " StopThread" evento para poder salir del bucle. Este evento se señala desde mi anulación Form.Dispose().

Leí en algún lugar que BackgroundWorkerrealmente está destinado a operaciones con las que no desea vincular la interfaz de usuario y tiene un fin finito, como descargar un archivo o procesar una secuencia de elementos. En este caso, el "final" es desconocido y solo cuando la ventana está cerrada. Por lo tanto, ¿sería más apropiado para mí usar un hilo de fondo en lugar de BackgroundWorkerpara este propósito?

Respuestas:


88

Según tengo entendido de su pregunta, está utilizando a BackgroundWorkercomo hilo estándar.

La razón por la que BackgroundWorkerse recomienda para cosas que no desea atar el hilo de la interfaz de usuario es porque expone algunos eventos agradables cuando se realiza el desarrollo de Win Forms.

A los eventos les gusta RunWorkerCompletedindicar cuándo el hilo ha completado lo que tenía que hacer, y el ProgressChangedevento para actualizar la GUI en el progreso de los hilos.

Entonces, si no está haciendo uso de estos, no veo ningún daño en usar un hilo estándar para lo que necesita hacer.


Otra cuestión de la que no estoy seguro es, supongamos que estoy tratando de eliminar el formulario que tiene el trabajador de fondo ejecutándose en él. Señalé el evento de apagado (ManualResetEvent) y, en algún momento, DoWork saldrá con gracia. ¿Debo dejar que el formulario siga adelante y desechar aunque el DoWork demore un poco más en terminar, o hay alguna forma (y es mejor) de enhebrar? Únase al trabajador en segundo plano hasta que realmente salga y luego deje el Dispose de la forma continuar?
freddy smith

Creo que BackgroundWorker.IsBusy es lo que estás buscando allí.
ParmesanCodice

1
Solo use CancelAsync(y pruebe CancellationPendingsi su hilo se sondeará en intervalos cortos, si desea que se genere una excepción en su lugar, use uno System.Threading.Thread.Abort()que genere una excepción dentro del bloque del hilo, elija el modelo correcto para la situación.
Brett Ryan

369

Algunos de mis pensamientos ...

  1. Use BackgroundWorker si tiene una única tarea que se ejecuta en segundo plano y necesita interactuar con la interfaz de usuario. La tarea de reunir datos y llamadas de método al hilo de la IU se maneja automáticamente a través de su modelo basado en eventos. Evite BackgroundWorker si ...
    • su ensamblaje no tiene o no interactúa directamente con la interfaz de usuario,
    • necesita que el hilo sea un hilo de primer plano, o
    • necesita manipular la prioridad del hilo.
  2. Use un hilo ThreadPool cuando desee eficiencia. ThreadPool ayuda a evitar la sobrecarga asociada con la creación, el inicio y la detención de subprocesos. Evite usar ThreadPool si ...
    • la tarea se ejecuta durante toda la vida de su aplicación,
    • necesitas que el hilo sea un hilo de primer plano,
    • necesita manipular la prioridad del hilo, o
    • necesita que el hilo tenga una identidad fija (abortar, suspender, descubrir).
  3. Utilice la clase Thread para tareas de larga duración y cuando requiera características ofrecidas por un modelo de subprocesamiento formal, por ejemplo, elegir entre subprocesos en primer plano y en segundo plano, ajustar la prioridad del subproceso, control detallado sobre la ejecución del subproceso, etc.

10
El trabajador en segundo plano está en el ensamblado System.dll y en el espacio de nombres System.ComponentModel. No hay dependencia en Winforms.
Kugel

17
Eso es correcto, pero BackgroundWorkerestá diseñado para informar el progreso del hilo a una parte interesada, lo que generalmente implica una interfaz de usuario. La documentación de MSDN para la clase lo deja muy claro. Si simplemente necesita realizar una tarea en segundo plano, prefiera usar un ThreadPoolhilo.
Matt Davis

55
Con respecto a su punto sobre la System.Windows.Formsasamblea; BackgroundWorkertambién es útil para aplicaciones WPF y esas aplicaciones pueden no tener una referencia a WinForms.
GiddyUpHorsey

12

Más o menos lo que dijo Matt Davis, con los siguientes puntos adicionales:

Para mí, el principal diferenciador BackgroundWorkeres la clasificación automática del evento completado a través de SynchronizationContext. En un contexto de UI, esto significa que el evento completado se dispara en el subproceso de UI y, por lo tanto, se puede usar para actualizar la IU. Este es un diferenciador importante si está utilizando el BackgroundWorkeren un contexto de interfaz de usuario.

Las tareas ejecutadas a través de ThreadPoolno se pueden cancelar fácilmente (esto incluye ThreadPool. QueueUserWorkItemY los delegados se ejecutan de forma asíncrona). Por lo tanto, si bien evita la sobrecarga de la conexión de subprocesos, si necesita cancelación, use una BackgroundWorkero (más probablemente fuera de la interfaz de usuario) gire un hilo y mantenga una referencia para que pueda llamar Abort().


1
Solo que ... con suerte, la aplicación está diseñada sobre un método limpio para detener una tarea enhebrada (Abortar generalmente no lo es)

11

También está atando un hilo de grupo de subprocesos durante la vida útil del trabajador en segundo plano, lo que puede ser preocupante ya que solo hay un número finito de ellos. Diría que si solo está creando el hilo una vez para su aplicación (y no está usando ninguna de las funciones del trabajador de fondo), use un hilo, en lugar de un hilo de trabajador de fondo / grupo de hilos.


1
Creo que este es un buen punto. Entonces, el mensaje que tomo de esto es usar un trabajador en segundo plano si necesita un hilo de fondo "temporalmente" durante la vida útil del Formulario, sin embargo, si necesita un hilo en segundo plano durante toda la vida útil del formulario (que podría ser minutos, horas, días ...) luego use un Thread en lugar de BackgroundWorker para no usar mal el propósito de ThreadPool
freddy smith

Con respecto a: "... lo que puede ser preocupante ya que solo hay un número finito de ellos", ¿quiere decir que otras aplicaciones en el sistema operativo pueden necesitarlos y compartirlos desde el mismo 'grupo'?
Dan W

8

Ya sabes, a veces es más fácil trabajar con un BackgroundWorker sin importar si estás usando Windows Forms, WPF o cualquier otra tecnología. La parte interesante de estos chicos es que tienes hilos sin tener que preocuparte demasiado sobre dónde se está ejecutando tu hilo, lo cual es ideal para tareas simples.

Antes de usar una BackgroundWorkerconsideración primero, si desea cancelar un hilo (cierre de la aplicación, cancelación del usuario), entonces debe decidir si su hilo debe verificar si hay cancelaciones o si se debe impulsar a la ejecución misma.

BackgroundWorker.CancelAsync()establecerá CancellationPendinga truepero no va a hacer nada más, es entonces la responsabilidad hilos para comprobar continuamente esto, tenga en cuenta también que usted podría terminar con una condición de carrera en este enfoque en el que el usuario ha cancelado, pero el hilo terminado antes de la prueba de CancellationPending.

Thread.Abort() por otro lado, arrojará una excepción dentro de la ejecución del subproceso que obliga a la cancelación de ese subproceso, sin embargo, debe tener cuidado con lo que podría ser peligroso si esta excepción surgiera repentinamente dentro de la ejecución.

El enhebrado necesita una consideración muy cuidadosa sin importar la tarea, para leer más:

Programación paralela en las mejores prácticas de subprocesos administrados de .NET Framework


5

Sabía cómo usar hilos antes de conocer .NET, así que me costó acostumbrarme cuando comencé a usar BackgroundWorkers. Matt Davis ha resumido la diferencia con gran excelencia, pero agregaría que es más difícil comprender exactamente lo que está haciendo el código, y esto puede dificultar la depuración. Es más fácil pensar en crear y cerrar hilos, IMO, que pensar en dar trabajo a un grupo de hilos.

Todavía no puedo comentar las publicaciones de otras personas, así que perdona mi cojera momentánea al usar una respuesta para abordar los muelles7

En su Thread.Abort();lugar, no use , señale un evento y diseñe su hilo para que finalice con gracia cuando se lo indique. Thread.Abort()plantea un ThreadAbortExceptionen un punto arbitrario en la ejecución del hilo, que puede hacer todo tipo de cosas infelices como monitores huérfanos, estado compartido corrupto, etc.
http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx


2

Si no está roto, arréglalo hasta que esté ... es broma :)

Pero en serio, BackgroundWorker es probablemente muy similar a lo que ya tienes, si hubieras comenzado desde el principio, tal vez hubieras ahorrado algo de tiempo, pero en este punto no veo la necesidad. A menos que algo no funcione, o piense que su código actual es difícil de entender, entonces me quedaría con lo que tiene.


2

La diferencia básica es, como usted dijo, generar eventos de GUI a partir de BackgroundWorker. Si el hilo no necesita actualizar la pantalla o generar eventos para el hilo principal de la GUI, entonces puede ser un hilo simple.


2

Quiero señalar un comportamiento de la clase BackgroundWorker que aún no se mencionó. Puede hacer que un subproceso normal se ejecute en segundo plano configurando la propiedad Thread.IsBackground.

Los hilos de fondo son idénticos a los hilos de primer plano, excepto que los hilos de fondo no impiden que un proceso finalice. [ 1 ]

Puede probar este comportamiento llamando al siguiente método en el constructor de su ventana de formulario.

void TestBackgroundThread()
{
    var thread = new Thread((ThreadStart)delegate()
    {
        long count = 0;
        while (true)
        {
            count++;
            Debug.WriteLine("Thread loop count: " + count);
        }
    });

    // Choose one option:
    thread.IsBackground = true; // <--- This will make the thread run in background
    thread.IsBackground = false; // <--- This will delay program termination

    thread.Start();
}

Cuando la propiedad IsBackground se establece en true y cierra la ventana, su aplicación finalizará normalmente.

Pero cuando la propiedad IsBackground se establece en false (de forma predeterminada) y cierra la ventana, solo desaparecerá la ventana pero el proceso seguirá ejecutándose.

La clase BackgroundWorker utiliza un subproceso que se ejecuta en segundo plano.


1

Un trabajador en segundo plano es una clase que funciona en un subproceso separado, pero proporciona una funcionalidad adicional que no se obtiene con un subproceso simple (como el manejo del informe de progreso de la tarea).

Si no necesita las funciones adicionales proporcionadas por un trabajador en segundo plano, y parece que no, un subproceso sería más apropiado.


-1

Lo que me deja perplejo es que el diseñador visual del estudio solo le permite usar BackgroundWorkers y Timers que en realidad no funcionan con el proyecto de servicio.

Le brinda controles de arrastrar y soltar en su servicio, pero ... ni siquiera intente implementarlo. No funciona

Servicios: solo use System.Timers.Timer System.Windows.Forms.Timer no funcionará aunque esté disponible en la caja de herramientas

Servicios: BackgroundWorkers no funcionará cuando se esté ejecutando como un servicio. Utilice System.Threading.ThreadPools en su lugar o llamadas asíncronas.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.