Callback

¿Qué es un callback? ¿Se suelen utilizar? ¿Qué me ofrecen? ¿Cómo se usan? Estas serán las preguntas más usuales que tendrá un usuario que no conoce esta técnica de programación. Vamos a ver qué podemos decir sobre ellos.

A la hora de definir callback, en la wikipedia encontramos lo siguiente:

Un callback es un segmento de código ejecutable, que es pasado como argumento a otro código.

Es decir, estamos dando la posibilidad de que un código ejecute otro, en caso de que lo considere necesario. ¿Y esto cuando se usa?

Imaginemos que nuestro programa está trabajando, y nosotros mientras siguimos haciendo nuestro trabajo con el programa. El programa esta haciendo X tarea en un hilo secundario, para así mantener el hilo principal (encargado de la GUI) libre, de manera que nosotros podemos seguir interactuando con él. Sin embargo, ese segundo hilo, según va realizando su tarea, podría necesitar informarnos de su avance, para que nosotros pudiésemos hacer algo con esa información. Por tanto, el segundo hilo necesita enviarnos esa información.

Para esta labor, podríamos emplear un callback, que por ejemplo almacenase esa información en una cola de mensajes recibidos.

Diagrama Callback

Diagrama Callback

 

De esta manera, el segundo hilo lanzaría la ejecución del código del callback, cuando ocurre el evento deseado (por ejemplo, terminar su ejecución, para comunicar que el proceso se ha completado).

Vale, hasta aquí todo claro. Pero, ¿cómo se utilizan? Pues veréis, en C# se emplean delegados, para realizar estos callback. ¿Qué es un delegado?

Un delegado es un tipo, que sirve para referenciar (apuntar) a un método.

Es decir, es un tipo (como integer, boolean, o cualquier otro, pero compuesto, no primitivo) que nos permitirá apuntar a un método ya creado. ¿Pero a cuál? A todos los que queramos, siempre que ya coincidan con lo que se denomina la firma del delegado: es decir, los tipos de los parámetros que recibe, y el tipo que devuelve.

No vamos a entrar en más características de los delegados, y vamos a pasar a ver cómo se usan estos elementos. Para ello, vamos a construir un programa en el que crearemos hilos secundarios, que lo único que harán será esperar X turnos de Y segundos, y cada vez que esperen un turno de los que tengan configurados, notificarán mediante un delegado su progreso al programa principal, que pintará estos progresos en una lista.

Vayamos por partes, primero mostremos la interfaz gráfica creada y sus elementos:

GUI de ejemplo para Callbacks

GUI de ejemplo para Callbacks

 

Donde hemos llamado a los dos textBox: turnosBox y segundosBox respectivamente, mientras que al botón lo llamaremos HiloButton.

Ahora, crearemos nuestro método que van a ejecutar los hilos que creemos:

Esperar(int turnos, int segundos, int indice)

Este método esperará el tiempo que hemos indicado anteriormente, y tras cada turno de espera notificará el progreso al hilo principal. Para ser exactos, actualizará directamente en el listview su progreso correspondiente, buscandose por el índice que se le ha pasado como argumento, en la primera de las columnas del listview.

Ahora bien, ¿cómo se crea y emplea el delegado? Primero necesitaremos la función que será llamada por el delegado: actualizarProgreso. Esta función será llamada por Esperar a modo de callback, siguiendo el diagrama anterior.

protected void actualizarProgreso(int indice, string progreso)

A continuación, debemos declarar el delegado que llamará a esta función:

public delegate void delegadoActProg(int a, string b)

Recordemos que el delegado puede llamar a todas aquellas funciones que coincidan con su firma, solo que nosotros solo vamos a emplearlo para una, que es lo que tenemos en nuestro proyecto.

Por último, veamos qué debe hacer Esperar para hacer un callback a actualizarProgreso mediante el delegado:

delegadoActProg callback = new delegadoActProg(actualizarProgreso);

callback(indice, progreso);

Siendo la segunda línea la invocación del delegado, pasándole los parámetros deseados (progreso es una variable de tipo string, e indice una variable de tipo int). Sin embargo, aquí hay un poco de trampa, ya que no se pueden modificar controles de la GUI desde otros subprocesos distintos al que los ha creado. Por tanto, es necesario invocar al control, indicándole que se desea realizar. Esta línea se sustituye pues, por:

this.Invoke(callback, new object[] { indice, progreso });

Donde estamos invocando la ejecución de nuestro delegado, con los parámetros deseados, pero haciendo que los ejecute el subproceso poseedor del objeto en cuestión. Dado que el objeto es this, y estamos en la clase del formulario, el objeto será el poseedor de la ventana de la GUI, que es nuestro objetivo.

Y ya está. El resto del código es dependiente del ejemplo que hemos creado, y lo podeis observar en el proyecto (mira un poco más abajo), que podéis descargar por Mega.

El resultado, después de introducir diversos hilos funcionando con distintas cantidades de turnos y segundos en espera es el siguiente:

Avance de los hilos

Avance de los hilos

 

Descarga del proyecto (Visual Studio 2010) aquí.

Referencias Útiles

Callback – Wikipedia

¿Qué es un callback? – StackOverflow

Usando Delegados – MSDN

Ejemplos de Hilos – MSDN

Llamadas de subprocesos a controles de la GUI – MSDN

Pasar parámetros a un hilo – StackOverflow

Clase Convert – MSDN

Deja un comentario

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