Trabajo en background

lunes, 8 de noviembre de 2010

BackgroundWorker


Durante la construcción de una aplicación es habitual encontrarnos con procesos que son muy lentos y no dan feedback al usuario. A continuación les mostramos una de las formas de resolverlo utilizando un control que nos brinda .Net a partir de su versión 2.0: el BackgroundWorker.

Caso de Uso

Una de nuestras aplicaciones tiene una pantalla donde se muestran filtros para obtener un informe que finalmente se graba en un archivo con formato Excel. Anteriormente, cuando el usuario apretaba el botón para iniciar el proceso, la pantalla se quedaba congelada hasta que la búsqueda de datos terminaba. En ese lapso de tiempo no se podía saber que estaba pasando: si se debía seguir esperando, o todo había dejado de funcionar. Aún informando “el proceso puede demorar”, después de unos instantes la aplicación comenzaba a mostrarse con el alerta de “(No Responde)”  y el usuario ya no sabía si seguir esperando, cancelar la ejecución de la aplicación, o en el peor de los casos reiniciar la computadora.

Por qué la decisión de usar el BackgroundWorker

Al comprobar que la performance de la búsqueda ya había sido optimizada desde el motor de bases de datos, llegamos a la conclusión de que debíamos hacer algo para que la aplicación no quede inutilizable mientras se genera el reporte. Sabiendo que lo más importante del reporte  es el resultado final, decidimos hacer todo el proceso de búsqueda de los datos  en un segundo plano y así lograr que la aplicación siga funcionando sin inconvenientes, incluso para realizar otras tareas que necesiten utilizar al mismo tiempo la base de datos de la que estoy esperando la respuesta para el reporte inicialmente deseado.
A pesar que no es ampliamente configurable y adaptable a todo tipo de necesidades, como podría serlo una solución que maneje varios hilos de ejecución desarrollada manualmente, el BackgroundWorker tiene una serie de ventajas muy prácticas para nuestra necesidad en particular y nos evita, entre otras cosas, planear la solución ideal para el problema y escribir “bastante” código para manejar hilos de ejecución lo cual es sumamente delicado ya que debemos tener en cuenta el ciclo de vida de cada hilo (y no perder ninguno en el camino),  la comunicación entre distintos hilos de ejecución, validar y manejar posibles errores, etc..
Todo esto sumado a que solo necesitamos realizar una única tarea indivisible, que consume mucho tiempo y no podríamos mejorar eso con más hilos de ejecución nos llevó a tomar la decisión de inclinarnos por el BackgroundWorker.

Manos a la obra

Veamos cómo en pocos pasos y sin mucho esfuerzo podemos obtener una aplicación que utilice varios hilos de ejecución.
Inicialmente nuestro código lucía así.  


image

Lo primero que debemos hacer es agregar un control BackgroudWorker en el designer del form y configurar los eventos a usar.  Básicamente en nuestro ejemplo utilizamos dos:


 BackgroundWorker, RunWorker

DoWork : Evento lanzado desde el código para realizar las tareas asincrónicas
RunWorkerCompleted : Evento lanzado por el control una vez finalizada la ejecución de las tareas asincrónicas.
Una vez hecho ésto lo único que resta hacer es reorganizar el código:



BackgroundWorker, RunWorker

Si observan con atención al hacer clic en el botón  invocamos al método RunWorkerAsync que se encarga del manejo de hilos y realización de tareas en background lanzando el evento DoWork.
Antes debemos verificar que el control no se encuentre trabajando actualmente, de esta forma evitamos un error.


BackgroundWorker, RunWorker

En el evento DoWork indicamos cuáles son las tareas que se ejecutan asincrónicamente, en nuestro caso hacemos el llamado a la base de datos.



BackgroundWorker, RunWorker

Vale aclarar que contamos con la opción de pasar como parámetro un objeto a través del RunWorkerAsync que es recibido dentro del DoWorkEventArgs. Esto puede ser de utilidad si necesitamos tener acceso al estado de objetos definidos en nuestro primer hilo de ejecución.  

Finalmente en el evento RunWorkerCompleted colocamos todas las tareas que  debemos realizar una vez que se ejecutaron todas las tareas asincrónicas. En nuestro ejemplo, como ya obtuvimos los datos debemos generar el reporte


image

Conclusión:

El BackgroundWorker nos brinda la posibilidad de que nuestras aplicaciones WinForms sean más “ágiles” y luzcan más “veloces“ en la realización de tareas que demandan demasiado tiempo, siendo además su implementación muy sencilla.