viernes, 5 de febrero de 2010

Método Join de la clase Thread

Ohh Hilos!, amados, odiados, útiles y a veces un poco complicados. Los hilos nos permiten realizar simultáneamente varias acciones al mismo tiempo, en una aplicación. Cuando trabajamos con interfaces de usuario, nos encontramos un problema.

Empecemos con un ejemplo sencillo, un simple hilo. Una vez que se ejecute la instrucción Start, entonces se va a comenzar a ejecutar. Ésto es útil, porque así el programa puede continuar ejecutando otras acciones y así, al hacer varias cosas al mismo tiempo, se supone que debería terminar antes de que si se ejecutaran en un solo hilo (típico divide y vencerás).


Uno podría hacer que cuando ese hilo termine se notifique de alguna manera al hilo principal, al usuario, etc. Pero eso es otro tema que podría complicarnos más de la cuenta, así que mejor lo dejamos para otro post. Lo que si necesitamos saber es que si necesitamos que nuestro hilo principal espere hasta que termine la ejecución del hilo que arrancamos, simplemente usamos el Método Thread.Join().

Imaginemos la siguiente situación: Tenemos una ventana y cuando le damos click a un botón una acción se va a llevar a cabo. Qué pasa cuando esa acción toma demasiado tiempo?. Si, adivinaron, la pantalla no se va a poder refrescar, es decir no se va a pintar correctamente y va aparecer en blanco o si le ponemos una ventana encima va a quedar pintada, y la pantalla se va a pintar correctamente hasta que termine el hilo, por lo que un Join no nos funciona.

Ahí es útil un hilo, para que la pantalla continúe pintándose y la acción se lleve a cabo, pero qué pasa si tenemos que esperar a que la acción termine? Estaríamos fritos, es por eso que también existe una sobrecarga que nos permite ejecutar una acción cada cierta cantidad de milisegundos, en este caso cada 100 millisegundos se ejecuta un DoEvents, que lo que hace es atender la cola de mensajes y en pocas palabras pintar nuestra ventana.

Pero hay que tener cuidado con el DoEvents, porque permite al usuario ejecutar cualquier acción permitida en la ventana, lo cual podría provocar un race condition o efectos no deseados. Lo ideal sería deshabilitar los controles, o poner una ventana modal que contenga un progress bar, lo cuál solucionaría el problema para muchos. Pero por ciertas razones no era lo que yo necesitaba, y eso lleva a la verdadera razón de este post. Cómo filtro los mensajes del sistema operativo, para que no se ejecute ningún evento en la ventana, pero que si se pinte?

Pues tuve que implementar un MessageFilter, de esta manera filtré todos los mensajes excepto el que implica refrescar la pantalla. Aquí les dejo el código completo.


Mas que todo, escribo esto para mis propias referencias futuras, pero si a alguno le sirve todavía mejor.