miércoles, 25 de junio de 2014

Receta No. 4-5 en C#: Ejecutar un Método por el Mecanismo de Señalización de un Objeto WaitHandle

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 Delegado WaitOrTimerCallback
3.2 Clase abstracta WaitHandle
3.2.1 Clase AutoResetEvent
3.3 Ejemplo
4. Práctica: Código C#
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

En esta receta vamos a explorar nuevos conceptos de mecanismos de señalización. Este tipo de mecanismos permite monitorear el instante en que un método debe iniciar su invocación. Esto puede ser de gran ayuda en la programación de tareas sincrónicas, es decir, de procesos que deben acceder a un área de memoria compartida uno tras otro evitando colisiones y corrupciones de las áreas de memoria en donde se alojan los datos compartidos por intentos de acceso simultáneos.

1. Problema

Necesitamos crear un proceso (método) que se ejecute automáticamente cada vez que se emita una señal por parte de una instancia de cualquier tipo que herede de la clase WaitHandle (N:System.Threading.WaitHandle).

2. Solución

La solución consiste en el siguiente ciclo básico para la creación de un método que se ejecute automáticamente:
  1. Declaración del método a ejecutar una vez se genere (emita) la señal.
  2. El método declarado debe poseer la firma del delegado System.Threading.WaitOrTimerCallback.
  3. Registro del delegado en el pool de threads con el método RegisterWaitForSingleObject (System.Threading.ThreadPool), además de la instancia WaitHandle (este posee el mecanismo de señalización de ejecución automática del método que encapsula el delegado).
Pasemos a la siguiente sección para explorar varios de estos artefactos de la biblioteca base de clases de .NET Framework y conocer sobre su uso y utilidad.

3. Discusión de la Solución

Describamos varios de los artefactos mencionados en la solución de la sección 2.

3.1 Delegado WaitOrTimerCallback

El delegado WaitOrTimerCallback [2] se halla en el nombre de espacio System.Threading. Este delegado encapsula un método que invocará o ejecutará cada vez que se emita una señal o se agote el intervalo de tiempo en que se puede ejecutar repetidamente el método callback. Firma:

public delegate void WaitOrTimerCallback(Object state, bool timedOut)

Descripción puntual:
  • Object state: contenedor de la información a usar por el método callback.
  • bool timedOut: true si el tiempo asociado a la instancia WaitHandle se agotó; false en caso de que se haya generado la señal de ejecución.
Como veremos en las siguientes secciones, una instancia de este delegado será pasada al método RegisterWaitForSingleObject de una instancia de un tipo WaitHandle, lo cual permitirá invocar el método callback (método encapsulado en el delegado WaitOrTimerCallback) una vez que el objeto WaitHandle genere la señal o se agote su tiempo.

3.2 Clase abstracta WaitHandle

La clase abstracta System.Threading.WaitHandle [3] encapsula objetos adyacentes al sistema operativo que se hayan en una cola de espera para acceder a recursos compartidos (e.g., dirección de memoria, archivos en disco duro, memoria virtual).

En [3] nos informan que este tipo de clase base se usa comúnmente para la sincronización de objetos para acceso controlado y en serie (uno tras otro) a un recurso compartido. Además:
"...Classes derived from WaitHandle define a signaling mechanism to indicate taking or releasing access to a shared resource, but use the inherited WaitHandle methods to block while waiting for access to shared resources."
El juego de métodos static que integran esta clase facilitan el bloqueo de un thread hasta que uno o más objectos bajo sincronizacion reciban una señal de desbloqueo.

3.2.1 Clase AutoResetEvent

La clase AutoResetEvent [5] notifica a un thread en lista de espera que un evento ha ocurrido. Se recomienda el uso de esta clase cuando un conjunto de threads requieren acceso exclusivo a un recurso compartido.

3.3 Método RegisterWaitForSingleObject (ThreadPool)

El método RegisterWaitForSingleObject [4] permite registrar un delegado (i.e., una instancia de WaitOrTimerCallback) para la ejecución por parte de un thread del pool de threads cada vez que cualquier tipo de objeto derivado de WaitHandle entre en estado de generación de señalas. (Con generación de señalas me refiero a la emisión de una señal, por ejemplo, cuando el estado de un recurso compartido ha cambiado a acceso disponible).

Inspeccionemos la firma de este método:

public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callBack, Object state, int millisecondsTimeOutIntervalbool executeOnlyOnce)

Descripción puntual:
  • Parámetros:
    • WaitHandle waitObject: registro del manejador del mecanismo de señalización.
    • WaitOrTimerCallback callBack: delegado a invocar cuando el parámetro waitObject genere una señal.
    • Object state: contenedor de información útil para el método encapsulado por el parámetro waitObject.
    • int millisecondsTimeOutInterval: intervalo de tiempo (en milisegundos) de ejecución del método callBack.
    • bool executeOnlyOnce: true para indicar el método callBack solo se ejecutará una vez; false para indicar que el temporizador (millisecondsTimeOutInterval)  se reiniciará por cada invocación del parámetro callBack.
  • Valor de retorno:
    • RegisteredWaitHandle: Representa al manejador del mecanismo de señalización.

3.4 Ejemplo

Este ejemplo pretende reunir el uso de los artefactos descritos en las tres últimas secciones. Se usará el delegado WaitOrTimerCallback para la representación de un método que se ejecutará cada vez que se emita una señal por parte de un instancia WaitHandle (en particular AutoResetEvent). (Este ejemplo fue encontrado y adaptado de [2])

Archivo C# UsoWaitOrTimerCallback.cs:

Creamos la clase InformacionTarea (líneas 8-12) para la contención de información útil para el método ProcesoEspera (líneas 53-76). Creamos una instancia de AutoResetEvent (línea 21) como manejador del mecanismo de señalización (false para indicar el estado inicial si generación de señal).

En las líneas 29-35 invocamos al método RegisterWaitForSingleObject al que le pasamos como argumentos el manejador del mecanismo de señalización creado en la línea 21, una instancia del delegado WaitOrTimerCallback que encapsula el método ProcesoEspera, un objeto como contenedor de información para el método ProcesoEspera, el intervalo de tiempo entre invocaciones del método ProcesoEspera (1000), y false para indicar que la invocación del método callback se ejecutará hasta que se genere una señal.

Con la expresión Thread.Sleep (3000) en el thread Main se hace una espera de 3 segundos para demostrar la repetición de 3 invocaciones del método ProcesoEspera. Luego, en la línea 42 se genera la señal de invocación del método ProcesoEspera. El cambio de este estado hace que en la línea 69 se des-registre el manejador del mecanismo de señalización.

Compilación:


  1. csc /target:exe UsoWaitOrTimerCallback.cs

Ejecución:


  1. .\UsoWaitOrTimerCallback.exe

> Prueba de ejecución.

Resultado:
Demostración uso del delegado WaitOrTimerCallback
Figura 1. Demostración uso del delegado WaitOrTimerCallback.

4. Práctica: Código C#

Este ejemplo demuestra la declaración de un método compatible con el delegado WaitOrTimerCallback, y el uso de un subtipo de WaitHandle: AutoResetEvent.

Compilación:


  1. csc /target:exe ManejadorSenializacion.cs

Ejecución:


  1. .\ManejadorSenializacion.exe

Resultado:
Creación de manejador de mecanismo de señalización AutoResetEvent
Figura 2. Creación de manejador de mecanismo de señalización AutoResetEvent.

5. Conclusiones

Creamos un sistema muy sencillo de manejo de sincronización. Esta receta introdujo los primeros conceptos básicos del manejo sincrónico de métodos a través del mecanismo de señalización que implementan los subtipos de la clase WaitHandle. Usamos el delegado WaitOrTimerCallback como encapsulador del método que se invocará cada vez que el manejador de señalización genere una señal o una vez haya transcurrido cierto tiempo (timeout).

6. Glosario


  • Delegado
  • Manejador de señalización
  • Método
  • Señal
  • Sincronización

7. Literatura & Enlaces

[1]: Visual C# 2010 Recipes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.
[2]: WaitOrTimerCallback Delegate (System.Threading) - http://msdn.microsoft.com/en-us/library/system.threading.waitortimercallback(v=vs.110).aspx
[3]: WaitHandle Class (System.Threading) - http://msdn.microsoft.com/en-us/library/system.threading.waithandle(v=vs.110).aspx
[4]: ThreadPool.RegisterWaitForSingleObject Method (WaitHandle, WaitOrTimerCallback, Object, Int32, Boolean) (System.Threading) - http://msdn.microsoft.com/en-us/library/w9f75h7a(v=vs.110).aspx
[5]: AutoResetEvent Class (System.Threading) - http://msdn.microsoft.com/en-us/library/vstudio/System.Threading.AutoResetEvent(v=vs.110).aspx


J

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.