run method fake create convert await async c# task-parallel-library backgroundworker .net-4.5 async-await

c# - method - Async/await vs BackgroundWorker



fake async c# (4)

Esta es una buena introducción: http://msdn.microsoft.com/en-us/library/hh191443.aspx La sección de subprocesos es justo lo que está buscando:

Los métodos asíncronos están destinados a ser operaciones sin bloqueo. Una expresión de espera en un método asíncrono no bloquea el hilo actual mientras se está ejecutando la tarea esperada. En cambio, la expresión firma el resto del método como una continuación y devuelve el control a la persona que llama del método async.

Las palabras clave async y await no provocan la creación de subprocesos adicionales. Los métodos asíncronos no requieren multithreading porque un método async no se ejecuta en su propio hilo. El método se ejecuta en el contexto de sincronización actual y usa el tiempo en la secuencia solo cuando el método está activo. Puede usar Task.Run para mover el trabajo vinculado a CPU a un hilo de fondo, pero un hilo de fondo no ayuda con un proceso que está esperando que los resultados estén disponibles.

El enfoque asincrónico de la programación asincrónica es preferible a los enfoques existentes en casi todos los casos. En particular, este enfoque es mejor que BackgroundWorker para operaciones vinculadas a 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 asincrónica es mejor que BackgroundWorker para las operaciones vinculadas a CPU porque la programación asincrónica separa los detalles de coordinación de ejecutar el código del trabajo que Task.Run transfiere al threadpool.

En los últimos días, he probado las nuevas características de .net 4.5 yc # 5.

Me gustan sus nuevas características async / await. Anteriormente había utilizado BackgroundWorker para manejar procesos más largos en segundo plano con una interfaz de usuario receptiva.

Mi pregunta es: después de tener estas nuevas características agradables, ¿cuándo debería usar async / await y cuando soy un BackgroundWorker ? ¿Cuáles son los escenarios comunes para ambos?


Esto es probable TL; DR para muchos, pero, creo que comparar await con BackgroundWorker es como comparar manzanas y naranjas y mis pensamientos sobre esto siguen:

BackgroundWorker está destinado a modelar una única tarea que le gustaría realizar en segundo plano, en un hilo del grupo de subprocesos. async / await es una sintaxis para esperar asincrónicamente en operaciones asincrónicas. Esas operaciones pueden o no usar un hilo de grupo de subprocesos o incluso usar cualquier otro subproceso . Entonces, son manzanas y naranjas.

Por ejemplo, puedes hacer algo como lo siguiente con la await :

using (WebResponse response = await webReq.GetResponseAsync()) { using (Stream responseStream = response.GetResponseStream()) { int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length); } }

Pero, probablemente nunca lo modelarías en un trabajador de segundo plano, probablemente harías algo como esto en .NET 4.0 (antes de await ):

webReq.BeginGetResponse(ar => { WebResponse response = webReq.EndGetResponse(ar); Stream responseStream = response.GetResponseStream(); responseStream.BeginRead(buffer, 0, buffer.Length, ar2 => { int bytesRead = responseStream.EndRead(ar2); responseStream.Dispose(); ((IDisposable) response).Dispose(); }, null); }, null);

Observe la disyuntiva de la eliminación comparada entre las dos sintaxis y cómo no puede usar sin async / await .

Pero, no harías algo así con BackgroundWorker . BackgroundWorker es generalmente para modelar una sola operación de larga ejecución que no desea afectar la capacidad de respuesta de la interfaz de usuario. Por ejemplo:

worker.DoWork += (sender, e) => { int i = 0; // simulate lengthy operation Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 1) ++i; }; worker.RunWorkerCompleted += (sender, eventArgs) => { // TODO: do something on the UI thread, like // update status or display "result" }; worker.RunWorkerAsync();

No hay nada allí que pueda usar async / await, BackgroundWorker está creando el hilo por usted.

Ahora, puedes usar TPL en su lugar:

var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext(); Task.Factory.StartNew(() => { int i = 0; // simulate lengthy operation Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 1) ++i; }).ContinueWith(t=> { // TODO: do something on the UI thread, like // update status or display "result" }, synchronizationContext);

En ese caso, TaskScheduler está creando el hilo por usted (suponiendo que TaskScheduler defecto), y podría usar TaskScheduler siguiente manera:

await Task.Factory.StartNew(() => { int i = 0; // simulate lengthy operation Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 1) ++i; }); // TODO: do something on the UI thread, like // update status or display "result"

En mi opinión, una comparación importante es si estás informando el progreso o no. Por ejemplo, puede tener un BackgroundWorker like este:

BackgroundWorker worker = new BackgroundWorker(); worker.WorkerReportsProgress = true; worker.ProgressChanged += (sender, eventArgs) => { // TODO: something with progress, like update progress bar }; worker.DoWork += (sender, e) => { int i = 0; // simulate lengthy operation Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 1) { if ((sw.Elapsed.TotalMilliseconds%100) == 0) ((BackgroundWorker)sender).ReportProgress((int) (1000 / sw.ElapsedMilliseconds)); ++i; } }; worker.RunWorkerCompleted += (sender, eventArgs) => { // do something on the UI thread, like // update status or display "result" }; worker.RunWorkerAsync();

Pero no trataría con algo de esto porque arrastraría y soltaría el componente de trabajador en segundo plano en la superficie de diseño de un formulario, algo que no puede hacer con async / await y Task ... ie no creará manualmente el objeto, establecerá las propiedades y configurará los manejadores de eventos. solo completaría el cuerpo de los controladores de eventos DoWork , RunWorkerCompleted y ProgressChanged .

Si "convertiste" eso a async / await, harías algo como:

IProgress<int> progress = new Progress<int>(); progress.ProgressChanged += ( s, e ) => { // TODO: do something with e.ProgressPercentage // like update progress bar }; await Task.Factory.StartNew(() => { int i = 0; // simulate lengthy operation Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 1) { if ((sw.Elapsed.TotalMilliseconds%100) == 0) { progress.Report((int) (1000 / sw.ElapsedMilliseconds)) } ++i; } }); // TODO: do something on the UI thread, like // update status or display "result"

Sin la capacidad de arrastrar un componente a una superficie Designer, depende del lector decidir cuál es "mejor". Pero, para mí, es la comparación entre await y BackgroundWorker , no si puedes esperar métodos Stream.ReadAsync como Stream.ReadAsync . por ejemplo, si estaba utilizando BackgroundWorker como estaba previsto, podría ser difícil convertirlo en await .

Otros pensamientos: http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html


BackgroundWorker está explícitamente etiquetado como obsoleto en .NET 4.5:

El artículo de MSDN "Programación asincrónica con Async y Await (C # y Visual Basic)" dice:

El enfoque asincrónico de la programación asincrónica es preferible a los enfoques existentes en casi todos los casos . En particular, este enfoque es mejor que BackgroundWorker para operaciones vinculadas a 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 asincrónica es mejor que BackgroundWorker para las operaciones vinculadas a CPU porque la programación asincrónica separa los detalles de coordinación de ejecutar el código del trabajo que Task.Run transfiere al threadpool

ACTUALIZAR

  • en respuesta al comentario de @ eran-otzap :
    "para operaciones vinculadas a IO porque el código es más simple y no tiene que protegerse contra las condiciones de carrera". ¿Qué condiciones de carrera pueden ocurrir? ¿Podría dar un ejemplo? "

Esta pregunta debería haberse puesto como una publicación separada.

Wikipedia tiene una buena explicación de racing condiciones de racing . La parte necesaria es multithreading y del mismo artículo de MSDN http://msdn.microsoft.com/en-us/library/hh191443.aspx :

Los métodos asíncronos están destinados a ser operaciones sin bloqueo. Una expresión de espera en un método asíncrono no bloquea el hilo actual mientras se está ejecutando la tarea esperada. En cambio, la expresión firma el resto del método como una continuación y devuelve el control a la persona que llama del método async.

Las palabras clave async y await no provocan la creación de subprocesos adicionales. Los métodos asíncronos no requieren multithreading porque un método async no se ejecuta en su propio hilo. El método se ejecuta en el contexto de sincronización actual y usa el tiempo en la secuencia solo cuando el método está activo. Puede usar Task.Run para mover el trabajo vinculado a CPU a un hilo de fondo, pero un hilo de fondo no ayuda con un proceso que está esperando que los resultados estén disponibles.

El enfoque asincrónico de la programación asincrónica es preferible a los enfoques existentes en casi todos los casos. En particular, este enfoque es mejor que BackgroundWorker para operaciones vinculadas a 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 asincrónica es mejor que BackgroundWorker para las operaciones vinculadas a CPU porque la programación asincrónica separa los detalles de coordinación de ejecutar el código del trabajo que Task.Run transfiere al threadpool

Es decir, "Las palabras clave de espera y no sincronizadas no provocan que se creen subprocesos adicionales".

Por lo que puedo recordar mis propios intentos cuando estaba estudiando este artículo hace un año, si corriste y jugaste con ejemplos de código del mismo artículo, podrías toparte con situaciones que no son asincrónicas (podrías intentar convertir a ti mismo) bloquear indefinidamente!

Además, para ejemplos concretos, puede buscar en este sitio. Aquí hay algunos ejemplos:


async / await está diseñado para reemplazar construcciones como BackgroundWorker . Si bien puede usarlo si lo desea, debería poder utilizar async / await, junto con algunas otras herramientas de TPL, para manejar todo lo que hay disponible.

Como ambos funcionan, todo se reduce a las preferencias personales en cuanto a cuándo se usa. ¿Qué es más rápido para ti ? ¿Qué es más fácil de entender para ti ?