c# wpf task-parallel-library backgroundworker async-await

c# - ¿No fue.NET 4.0 TPL lo que hizo que los patrones asíncronos de APM, EAP y BackgroundWorker quedaran obsoletos?



wpf task-parallel-library (3)

Tengo 2 tipos de proyectos de aplicaciones C # WPF:

  • basado en .NET 4.0 que no puedo migrar a .NET 4.5
  • basado en .NET 4.0 que puedo migrar a .NET 4.5

Todos ellos deben generar 2-10 procesos de larga duración (días) que pueden ser cancelados y relanzados por los usuarios.

Estoy interesado en seguir las mejores prácticas de diseño. En primer lugar, ahora, estoy interesado en desmarcar el uso de BackgroundWorker , aunque espero que mi pregunta sea válida sobre otros patrones asíncronos.

Veo (contradiciendo) puntos de vista concurrentes sobre

Patrones asíncronos:

"El enfoque asíncrono de la programación asíncrona es preferible a los enfoques existentes en casi todos los casos. En particular, este enfoque es mejor que el de BackgroundWorker para las operaciones de enlace IO porque el código es más simple y no tiene que protegerse contra las condiciones de carrera. "En combinación con Task.Run, la programación asíncrona es mejor que BackgroundWorker para las operaciones vinculadas a la CPU porque la programación asíncrona separa los detalles de coordinación de la ejecución de su código del trabajo que Task.Run transfiere a la agrupación de hilos"

  • B) Ellos (o, al menos, BackgroundWorker ) no están obsoletos en .NET 4.5

Todavía estoy en duda:

  1. ¿Son esos patrones (en primer lugar, BGW) obsoletos en .NET 4.5?
  2. Si están obsoletos en .NET 4.5, ¿por qué no están obsoletos en .NET 4.0?

    2A) ¿Entiendo incorrectamente que las nuevas características de .NET 4.5 son todavía "fáciles" de implementar / reproducir en .NET 4.0?


Considero que estos patrones (APM, EAP y BGW en particular) están obsoletos en .NET 4.5. La combinación de async con Task.Run es superior a BGW en todos los sentidos. De hecho, acabo de comenzar una serie en mi blog donde compararé BGW con Task.Run y mostraré cómo es más engorroso en cada situación; hay algunas situaciones donde es un poco más engorroso, pero hay otras situaciones donde es mucho más engorroso.

Ahora, si están obsoletos en .NET 4.0 es otra pregunta completamente. De sus otras publicaciones, está hablando de desarrollar para .NET 4.0 con VS2010 , por lo que el backport Microsoft.Bcl.Async no es una opción. En ese caso, ni APM ni EAP pueden considerarse IMO obsoletos. En esta plataforma, puede considerar Task.Factory.StartNew como una alternativa a BGW, pero BGW tiene algunas ventajas cuando se trata de informes de progreso y cálculo de subprocesos automático de sus eventos de progreso y finalización.

Actualización: Actualicé recientemente una antigua publicación de blog donde discuto varias implementaciones de operaciones en segundo plano . En esa publicación, cuando hablo de "Tareas (métodos async )", me refiero a usar Task con todo el soporte async .NET 4.5, Task.Run , etc. La sección "Tareas (Biblioteca paralela de tareas)" evalúa la Task como existía en .NET 4.0.


Generalmente recomiendo Task y / o await si uso .NET 4.5. Pero Task & BGW tiene 2 escenarios claramente diferentes. La tarea es buena para las tareas generales asíncronas cortas que podrían ser encadenadas a una continuación y la espera es buena en las tareas que de forma implícita se están devolviendo al hilo de UI. BGW es bueno para una sola operación larga que no debería afectar la capacidad de respuesta de su interfaz de usuario. Puede arrastrar y soltar un BGW en la superficie de diseño y hacer doble clic para crear controladores de eventos. No tiene que lidiar con LongRunning o ConfigureAwait si no quiere LongRunning de otro hilo. Muchos encuentran el progreso de BGW más fácil que IProgress<T> .

Aquí hay algunos ejemplos de cómo usar ambos en un escenario de "operación prolongada":

Dado que la pregunta menciona específicamente .NET 4.0, el siguiente es un código simple que utiliza una Task para realizar una operación prolongada mientras proporciona progreso a una IU:

startButton.Enabled = false; var task = Task.Factory. StartNew(() => { foreach (var x in Enumerable.Range(1, 10)) { var progress = x*10; Thread.Sleep(500); // fake work BeginInvoke((Action) delegate { progressBar1.Value = progress; }); } }, TaskCreationOptions.LongRunning) .ContinueWith(t => { startButton.Enabled = true; progressBar1.Value = 0; });

Un código similar con BackgroundWorker podría ser:

startButton.Enabled = false; BackgroundWorker bgw = new BackgroundWorker { WorkerReportsProgress = true }; bgw.ProgressChanged += (sender, args) => { progressBar1.Value = args.ProgressPercentage; }; bgw.RunWorkerCompleted += (sender, args) => { startButton.Enabled = true; progressBar1.Value = 0; }; bgw.DoWork += (sender, args) => { foreach (var x in Enumerable.Range(1, 10)) { Thread.Sleep(500); ((BackgroundWorker)sender).ReportProgress(x * 10); } }; bgw.RunWorkerAsync();

Ahora, si estuviera usando .NET 4.5, podría usar Progress<T> lugar de la llamada BeginInvoke con la Task . Y como en 4.5, el uso de await probablemente sería más legible:

startButton.Enabled = false; var pr = new Progress<int>(); pr.ProgressChanged += (o, i) => progressBar1.Value = i; await Task.Factory. StartNew(() => { foreach (var x in Enumerable.Range(1, 10)) { Thread.Sleep(500); // fake work ((IProgress<int>) pr).Report(x*10); } }, TaskCreationOptions.LongRunning); startButton.Enabled = true; progressBar1.Value = 0;

El uso de Progress<T> significa que el código no está acoplado a un marco de UI específico (es decir, la llamada a BeginInvoke ) de la misma manera que BackgroundWorker facilita el desacoplamiento de un marco de UI específico. Si no le importa, entonces no necesita introducir la complejidad adicional de usar Progress<T>

En cuanto a LongRunning , como dice Stephen Toub: "Por lo general, solo usaría LongRunning si, a través de las pruebas de rendimiento, descubriera que no usarlo provocaba grandes demoras en el procesamiento de otro trabajo", por lo que si encuentra que necesita usarlo, entonces usted lo usa: existe el análisis agregado o simplemente la "complejidad" de agregar siempre el parámetro LongRunning . No utilizar LongRunning significa que el subproceso del conjunto de subprocesos utilizado para la operación de ejecución prolongada no se podrá utilizar para otras tareas más transitorias y podría forzar al conjunto de subprocesos a retrasar el inicio de una de estas tareas transitorias mientras inicia otro subproceso (al menos un segundo).

No hay atributos en el marco que específicamente digan que BGW (o EAP, o APM) están en desuso . Entonces, depende de usted decidir dónde y cuándo cualquiera de estas cosas está "obsoleta". BGW, en particular, siempre tuvo un escenario de uso muy específico que aún se aplica a él. Tienes alternativas bastante decentes en .NET 4.0 y 4.5; pero realmente no creo que BGW sea "obsoleta".

No estoy diciendo que use siempre BackgroundWorker , solo digo que antes de que desapruebe automáticamente a BackgroundWorker, en algunos casos podría ser una mejor opción.


Mi pregunta fue:

¿No fue .NET 4.0 TPL lo que hizo que los patrones asíncronos de APM, EAP y BackgroundWorker quedaran obsoletos?

es decir, una duda, pidiendo confirmación o negación de que si algo está obsoleto en .NET 4.5, entonces no veo por qué y cómo puede ser diferente en .NET 4.0, es decir, con la biblioteca paralela de tareas (sin compromiso async/await y .NET 4.5 soporte exclusivamente)

Y me complace mucho encontrar las respuestas de Nick Polyak en su artículo de proyecto de código "Task Parallel Library and async-await Funcionalidad - Patrones de uso en muestras fáciles", declaró:

  • "Gran parte de la funcionalidad de .NET 4.5 se ha cambiado a Tareas y BackgroundWorker está casi obsoleto ahora , pero es posible que aún surja algunos casos en los que es necesario usar el patrón EAP. En tales casos, será bueno producir objetos de Tarea de la funcionalidad EAP para que pueda programar su ejecución en paralelo o esperar a que finalicen algunas tareas previas, etc. En esta sección le mostramos cómo lograrlo "
  • "Al tener la funcionalidad de Tarea, la funcionalidad de BackgroundWorker vuelto más obsoleta: puede lograr lo que necesite usando una Tarea en lugar de un BackgroundWorker . Aun así, puede haber alguna razón por la que quiera usar la funcionalidad de BackgroundWorker en el equipo, ya sea porque su código heredado lo usa o porque la mayoría a los miembros de su equipo o a su jefe les encanta y lo entienden mejor que la nueva funcionalidad de Tarea "

Todavía está abierto para ver cualquier punto de vista argumentado diferente