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:
A) .NET 4.5 los hizo obsoletos.
- nombrado como tal en el libro Por Joseph Albahari, Ben Albahari "C # 5.0 en pocas palabras: la referencia definitiva" en el subcapítulo " Patrones obsoletos ", mientras que su edición anterior "" C # 4.0 en pocas palabras: la referencia definitiva "no lo hizo
- El artículo de MSDN "Programación asíncrona con asíncrono y espera (C # y Visual Basic)" dice:
"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:
- ¿Son esos patrones (en primer lugar, BGW) obsoletos en .NET 4.5?
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 deBackgroundWorker
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