Published articles on other web sites*

Published articles on other web sites*

Backgroundworker en C#


Productividad y eficiencia desarrollando en .NET

Nota: Ejemplo completo de uso del control BackgroundWorker. El artículo inicial se creó en inglés y fue publicado aquí. En vista del éxito que ha tenido lo traduzco al castellano y lo republico también en este apartado.

Introducción


El control BackgroundWorker fue introducido en el .NET Framework 2.0 y permite realizar operaciones costosas o duraderas en un hilo diferente al del interfaz, de manera que la aplicación sigue respondiendo al usuario mientras este trabajo se procesa en segundo plano.


Usando este control, la gestión de hilos está encapsulada en el control de manera que el programador no tiene que lidiar con hilos (threads), invokes o delegados (delegates).
Ejemplo


Este sencillo ejemplo cubre prácticamente todas las posibilidades de este componente: soporte de cancelación, gestión de errores , información de progreso (y ejemplo de como pasar información en cada notificación de progreso)
Ejemplo Background Worker

Evento backgroundWorker1_DoWork


El evento backgroundWorker1_DoWork es desencadenado por el control cuando
el método RunWorkerAsync()es invocado. Este método se ejecuta en un segundo hilo que crea el control BackgroundWorker. Al no ser el hilo principal del interfaz, no intentes acceder a ningún control aquí, o si lo haces utiliza para ello un delegado.
01//[System.Diagnostics.DebuggerNonUserCodeAttribute()]  (*)
02private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
03{
04    //no usaremos código try/catch aquí, a no ser que después hagamos un throw de la excepción capturada.
05    //es necesario dejar que el backgroundworker sea quien capture cualquier excepción producida.
06    //si se produce una excepción, el control la disponibilizará una vez haya finalizado su ejecución,
07    //y disparado el evento "backgroundWorker1_RunWorkerCompleted"
08    //the RunWorkerCompletedEventArgs object, method backgroundWorker1_RunWorkerCompleted
09    //try
10    //{
11        DateTime start = DateTime.Now;
12        e.Result = "";
13        for (int i = 0; i < 100; i++)
14        {
15            System.Threading.Thread.Sleep(50); //simulamos trabajo
16
17           //hemos completado un porcentaje del trabajo previsto, luego notificamos de ello.
18            backgroundWorker1.ReportProgress(i, DateTime.Now);
19
20            //descomenta este código para ver como esta excepción es gestionada por el
21            //control backgroundworker
22            //descomenta también el atributo indicado arriba para evitar que el depurador
23            //pare en la excepción, ya que queremos simular
24            //el comportamiento del control en tiempo de ejecución.
25            //if (i == 34)
26            //    throw new Exception("something wrong here!!");
27
28            //en caso de que soporte cancelación y el control haya recibido la petición de cancelación,
29            //cancelamos el trabajo. Es la manera en la que realizamos una salida "limpia".
30            if (backgroundWorker1.CancellationPending)
31            {
32                e.Cancel = true;
33                return;
34            }
35        }
36
37        TimeSpan duration = DateTime.Now - start;
38
39        //aquí podríamos devolver información de utilidad, como el resultado de un cálculo,
40        //número de elementos afectados, etc.. de manera sencilla y segura
41        //al hilo principal
42        e.Result = "Duration: " + duration.TotalMilliseconds.ToString() + " ms.";
43    //}
44    //catch(Exception ex){
45    //    MessageBox.Show("Don't use try catch here!");
46    //}
47}


(*) Nota: El atributo [System.Diagnostics.DebuggerNonUserCodeAttribute()] es necesario para evitar que el depurador interrumpa el flujo de la ejecución en caso de que una excepción se produzca. De esta manera podemos trabajar en modo depuración con el mismo comportamiento que el control tendría en modo ejecución. Este comportamiento consistiría en capturar internamente la excepción (por eso no usaremos try/catch en el método) y devolvernos la misma junto con el resultado, en el método backgroundWorker1_RunWorkerCompleted
Evento backgroundWorker1_ProgressChanged


Este evento es lanzado en el hilo principal, por lo que aquí SI podemos acceder a controles del formulario de manera segura.
01private void backgroundWorker1_ProgressChanged(object sender,
02ProgressChangedEventArgs e)
03{
04   progressBar1.Value = e.ProgressPercentage; //actualizamos la barra de progreso
05   DateTime time = Convert.ToDateTime(e.UserState); //obtenemos información adicional si procede
06
07   //en este ejemplo, logamos a un textbox
08   txtOutput.AppendText(time.ToLongTimeString());
09   txtOutput.AppendText(Environment.NewLine);
10}

Evento backgroundWorker1_RunWorkerCompleted


Este método se ejecuta cuando la tarea ha sido finalizada. Una tarea se finaliza cuando:
a) termina de manera normal
b) termina con error
c) es cancelada.


En el objeto de tipo RunWorkerCompletedEventArgs encontraremos información detallada del tipo de finalización, así como los datos que hemos podido pasar en la propiedad Result.
01private void backgroundWorker1_RunWorkerCompleted(object sender,
02RunWorkerCompletedEventArgs e)
03{
04   if (e.Cancelled) {
05     MessageBox.Show("The task has been cancelled");
06   }
07   else if (e.Error != null)
08   {
09     MessageBox.Show("Error. Details: " + (e.Error as Exception).ToString());
10   }
11   else {
12     MessageBox.Show("The task has been completed. Results: " + e.Result.ToString());
13   }
14}

Enviado las órdenes de inicio y cancelación al control

Notificamos al control backgroundworker de que cancele el proceso.
1private void btoCancel_Click(object sender, EventArgs e)
2{
3   //este código no mata ni afecta al hilo en el que se está ejecutando el procesamiento.
4   //Sirve a efectos de notificación, que debe de ser
5   //gestionada de la manera que se indica arriba en el ejemplo
6   backgroundWorker1.CancelAsync();
7}
Lanzamos el trabajo. En este momento, el control backgroundworker lanza un hilo en el que ejecuta la tarea asignada, a través del método backgroundWorker1_DoWork visto anteriormente.
1private void btoStart_Click(object sender, EventArgs e)
2{
3backgroundWorker1.RunWorkerAsync();
4}

No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...