Saltar al contenido

Async / await vs BackgroundWorker

Solución:

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

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

Por ejemplo, puede hacer algo como lo siguiente con await:

using (WebResponse response = await webReq.GetResponseAsync())

    using (Stream responseStream = response.GetResponseStream())
    
        int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
    

Pero, probablemente nunca modelarías eso en un trabajador en 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 falta de articulación de la disposición en comparación entre las dos sintaxis y cómo no puede usar using sin async/await.

Pero, no harías algo así con BackgroundWorker. BackgroundWorker suele ser para modelar una única operación de larga duración que no desea que afecte 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();

Realmente no hay nada con lo que puedas usar async / await, BackgroundWorker está creando el hilo para ti.

Ahora, podría 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 cuyo caso el TaskScheduler está creando el hilo para usted (asumiendo el valor predeterminado TaskScheduler), y podría usar await como sigue:

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 informa sobre el progreso o no. Por ejemplo, puede tener un BackgroundWorker like esta:

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 lidiaría con algo de esto porque arrastraría y soltaría el componente de trabajo de fondo en la superficie de diseño de un formulario, algo que no puede hacer con async/await y Task... es decir, no creará manualmente el objeto, establecerá las propiedades y establecerá los controladores de eventos. solo llenarías el cuerpo del DoWork, RunWorkerCompleted, y ProgressChanged controladores de eventos.

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

     IProgress progress = new Progress();

     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 de Designer, realmente depende del lector decidir cuál es "mejor". Pero, para mí, es la comparación entre await y BackgroundWorker, no si puede esperar métodos integrados como Stream.ReadAsync. por ejemplo, si estuvieras usando BackgroundWorker según lo previsto, podría ser difícil convertir para usar await.

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

async / await está diseñado para reemplazar construcciones como la BackgroundWorker. Mientras que ciertamente pueden utilícelo si lo desea, debería poder usar async / await, junto con algunas otras herramientas de TPL, para manejar todo lo que hay por ahí.

Dado que ambos funcionan, todo depende de la preferencia personal en cuanto a cuál usar y cuándo. ¿Qué es más rápido para usted? ¿Qué es más fácil para usted ¿comprender?

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

Los métodos asincrónicos están pensados ​​para ser operaciones sin bloqueo. Una expresión de espera en un método asíncrono no bloquea el hilo actual mientras se ejecuta la tarea esperada. En cambio, la expresión registra el resto del método como una continuación y devuelve el control al llamador del método asíncrono.

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

El enfoque asincrónico para 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 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 al grupo de subprocesos.

Aquí puedes ver las comentarios y valoraciones de los usuarios

Recuerda que tienes la capacidad de comentar .

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *