BackgroundWorker vs background Tema

Tengo una pregunta estilística sobre la elección de la implementación del hilo de fondo que debería usar en una aplicación de formulario de Windows. Actualmente tengo un BackgroundWorker en un formulario que tiene un ciclo infinito (while(true)) . En este ciclo, uso WaitHandle.WaitAny para mantener el hilo durmiendo hasta que algo interesante pase. Uno de los identificadores de evento que espero es un evento ” StopThread ” para poder salir del bucle. Este evento se señaliza cuando de mi Form.Dispose() invalidado. Form.Dispose() .

Leí en alguna parte que BackgroundWorker está realmente destinado a operaciones con las que no desea atar 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 BackgroundWorker para este propósito?

Según entiendo tu pregunta, estás utilizando un BackgroundWorker como hilo estándar.

La razón por la que se recomienda el uso de BackgroundWorker para las cosas que no desea atar al hilo de la interfaz de usuario es porque expone algunos eventos agradables al hacer el desarrollo de Win Forms.

Eventos como RunWorkerCompleted para indicar cuándo el subproceso completó lo que tenía que hacer, y el evento ProgressChanged para actualizar la GUI en el progreso de los subprocesos.

Entonces, si no los usa, no veo ningún daño al usar un subproceso estándar para lo que necesita hacer.

Algunos de mis pensamientos …

  1. Utilice BackgroundWorker si tiene una única tarea que se ejecuta en segundo plano y necesita interactuar con la interfaz de usuario. La tarea de reunir las llamadas de datos y métodos al subproceso de la interfaz de usuario 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,
    • necesitas que el hilo sea un hilo de primer plano, o
    • necesitas manipular la prioridad del hilo.
  2. Use un hilo ThreadPool cuando se desee eficiencia. ThreadPool ayuda a evitar la sobrecarga asociada con la creación, inicio y detención de subprocesos. Evite usar el ThreadPool si …
    • la tarea se ejecuta durante toda la vida de su aplicación,
    • necesitas que el hilo sea un hilo de primer plano,
    • necesitas manipular la prioridad del hilo, o
    • necesita que el hilo tenga una identidad fija (abortar, suspender, descubrir).
  3. Use la clase Thread para tareas de larga ejecución y cuando requiera características ofrecidas por un modelo de threading formal, por ejemplo, eligiendo entre hilos de primer plano y de fondo, ajustando la prioridad del hilo, control detallado sobre la ejecución del hilo, etc.

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

Para mí, el principal diferenciador con BackgroundWorker es la clasificación automática del evento completo a través de SynchronizationContext . En un contexto de interfaz de usuario, esto significa que el evento completo se desencadena en el hilo de la interfaz de usuario, por lo que se puede utilizar para actualizar la interfaz de usuario. Este es un gran diferenciador si está utilizando BackgroundWorker en un contexto de interfaz de usuario.

Las tareas ejecutadas a través de ThreadPool no se pueden cancelar fácilmente (esto incluye ThreadPool . QueueUserWorkItem y los delegates se ejecutan asincrónicamente). Así que, aunque evita la sobrecarga de subprocesos de subprocesos, si necesita cancelación, use un BackgroundWorker o (más probablemente fuera de la interfaz de usuario), haga un giro y conserve una referencia para poder llamar a Abort() .

También está atando un hilo de subprocesos durante toda la vida del trabajador en segundo plano, lo que puede ser motivo de preocupación ya que solo hay un número limitado de ellos. Diría que si solo creas el hilo una vez para tu aplicación (y no utilizas ninguna de las funciones del trabajador en segundo plano), entonces usa un hilo, en lugar de un hilo backgroundworker / threadpool.

Ya sabes, a veces es más fácil trabajar con un BackgroundWorker independientemente de si estás usando Windows Forms, WPF o la tecnología que sea. La parte clara de estos tipos es que se enruta sin tener que preocuparse demasiado por dónde se está ejecutando el hilo, lo cual es ideal para tareas simples.

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

BackgroundWorker.CancelAsync() configurará CancellationPending en true pero no hará nada más, es responsabilidad de los hilos comprobar continuamente esto, tenga en cuenta también que podría terminar con una condición de carrera en este enfoque donde su usuario canceló, pero el hilo se completó antes de la prueba de CancellationPending .

Thread.Abort() por otro lado arrojará una excepción dentro de la ejecución de la ejecución de subprocesos que impone la cancelación de ese subproceso, pero debes tener cuidado con lo que podría ser peligroso si esta excepción surgiera de repente dentro de la ejecución.

Threading necesita una consideración muy cuidadosa sin importar cuál sea la tarea, para leer más:

Progtwigción paralela en las mejores prácticas de enrutamiento gestionado de .NET Framework

Sabía cómo usar hilos antes de conocer .NET, por lo que me tomó un tiempo 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 qué 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 conjunto de hilos.

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

No use Thread.Abort (); en su lugar, señale un evento y diseñe su hilo para que termine con elegancia cuando se lo indique. Thread.Abort () genera una excepción ThreadAbortException en un punto arbitrario en la ejecución del subproceso, que puede hacer todo tipo de cosas desagradables como Monitores huérfanos, estado compartido dañado, y así sucesivamente. http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx

Si no está roto, arréglenlo hasta que esté … es broma 🙂

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

La diferencia básica es, como usted indicó, generar eventos de GUI desde 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.

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

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

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

Los subprocesos de fondo son idénticos a los subprocesos de primer plano, excepto que los subprocesos 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 = false; // <--- This will make the thread run in background thread.IsBackground = true; // <--- This will delay program termination thread.Start(); } 

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

Pero cuando la propiedad IsBackground se configura como falsa (de forma predeterminada) y se cierra la ventana, solo se desactivará la ventana, pero el proceso seguirá ejecutándose.

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

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

Le brinda controles precisos de arrastrar y soltar en su servicio, pero … ni siquiera intente implementarlo. No funcionará

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 Use System.Threading.ThreadPools en su lugar o llamadas Async