miércoles, 9 de febrero de 2011

C#: Tareas multihilo en dos patás.

- Introducción.

Como probablemente ya sabes, hoy en día es importante construir tus aplicaciones de forma que aprovechen los múltiples "cores" de los procesadores y que sean capaces de seguir respondiendo al usuario aunque "por detrás" estén ejecutando procesos pesados.

En los viejos tiempos (más o menos, a principios del año pasado) , los programadores de C# usábamos el objeto BackgroundWorker para construir aplicaciones multihilo. Con la llegada de .NET 4, tenemos una nueva herramienta: los objetos Task.

A continuación encontrarás algunos fragmentos de código útiles para crear tareas y organizar su ejecución. Dejo de lado el tratamiento de excepciones y el paso de parámetros para otro artículo.

- Para crear una tarea.

Imagínate que tienes un formulario, y que quieres ejecutar una tarea cuando se carga (por ejemplo, leer unos datos de la base de datos).

using System.Threading.Tasks;
....
private void Callejero_Load(object sender, EventArgs e)


{
     Task t1 = new Task(
     () =>
           {
                  // Cargamos la lista de provincias en el control.
           });


      t1.Start();
}

Este  método, define una tarea, solicita que se empiece a ejecutar, y sale sin esperar a que termine. El formulario seguirá respondiendo al usuario mientras ejecuta otras tareas en segundo plano.

Una cosa importante es que no puedes acceder a los controles del formulario desde un hilo distinto al que lo ha creado. Por ejemplo, la tarea no podrá cambiar el valor de una caja de texto del formulario directamente. Más adelante veremos un ejemplo de cómo hacerlo.


- Definir una tarea que se ejecute cuando otra termine.

Si tenemos una tarea T1 (como la anterior), y queremos definir una tarea T2 que se ejecute a continuación...


Task t2 = t1.ContinueWith(
ant =>
{
           // Hacer más cosas
});


No es necesario llamar a Start() en una tarea de continuación (de hecho da un error). La tarea se arrancará automáticamente cuando T1 termine.

- Definir una tarea que se ejecute cuando varias tareas terminen.

Si tenemos dos tareas T1 y T2 que se van a ejecutar en paralelo, y queremos definir una tarea T3 que arranque cuando las otras dos terminen ...


Task t3 = Task.Factory.ContinueWhenAll(
       new[] { t1, t2 },
        tasks =>
              {
                   // Hacemos más cosas
              }
 );

- Definir una tarea que se ejecute cuando otra termina, y actualice controles de nuestro formulario WPF.

Este es un caso muy típico. Imagínate que tienes un formulario que, cuando pulsas un botón, arranca una tarea T1. Cuando la tarea empieza, apagas un botón para que el usuario no le vuelva a dar. Cuando la tarea termina, quieres volver a habilitarlo. Nada más fácil.


Task t_final = t_1.ContinueWith(
        ant =>
       {
              btn_procesar.IsEnabled = true;
       }, TaskScheduler.FromCurrentSynchronizationContext());




- Definir una tarea que se ejecute cuando terminan otras, y actualice controles de nuestro formulario.



Task tarea_final = Task.Factory.ContinueWhenAll(
                        new[] { task_eventos, task_estados, task_info, task_parametros, task_control, task_canont },
                        tasks =>
                        {
                            // Accedemos a controles de nuestro formulario.
                        }, 
                        CancellationToken.None, 
                        TaskContinuationOptions.None,
                        TaskScheduler.FromCurrentSynchronizationContext()
                    );

No hay comentarios:

Publicar un comentario