miércoles, 23 de julio de 2014

Receta C# No. 4-10: Sincronización de Múltiples Threads usando un Semáforo

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
4. Práctica: Código C#
5. Artefactos
6. Conclusiones
7. Glosario
8. Literatura & Enlaces

0. Introducción

Exploraremos el cuarto método de sincronización de threads: Monitor, Event, Mutex, y Semaphore. Descubriremos que este mecanismo de sincronización permite le ejecución en paralelo de varios threads sobre una región crítica de código. Demostraré el uso de la clase Semaphore para la sincronización de varios threads que se ejecutan uno tras otro. En la segunda demostración, usaremos el constructor sobrecargado de Semaphore para ejecutar hasta dos threads en paralelo sobre la región crítica.

1. Problema

Queremos controlar la ejecución en paralelo de varios threads cuando estos intenten acceder a un recurso compartido, y de esta manera evitar la corrupción de datos.

2. Solución

En la biblioteca base de clases de .NET Framework encontramos la clase Semaphore para controlar el acceso concurrente a un acceso compartido. El nombre de espacio para esta clase es System.Threading.

3. Discusión de la Solución

La clase Semaphore [2] constituye una implementación de código administrado de .NET Framework para el mecanismo de sincronización de threads conocido como Semáforo. A diferencia de los mecanismos presentados en recetas anteriores (Monitor, Event, y Mutex), con Semaphore podemos establecer la cantidad de unidades de trabajo o threads requerimos para completar una tarea de una región crítica, es decir, que podemos tener uno o más threads en ejecución sobre un recurso compartido (e.g., archivo, conexión a base de datos).

Continuando, la clase Semaphore hereda de la clase WaitHandle, esto indica que el método WaitOne lo podemos utilizar para bloquear el acceso a la región crítica.


Un semáforo posee un contador que lleva la cuenta de los threads que se hayan sobre la región crítica. Cada vez que un thread pasa por el semáforo este contador se decrementa en una unidad, y la operación contraria ocurre cuando el thread libera la región crítica. Al final, cuando todos los threads han liberado el semáforo, el contador es el valor máximo especificado en uno de los constructores sobrecargados de la clase Semaphore.


En [1] nos advierten que:
«There is no guaranteed order; such as FIFO or LIFO, in which blocked threads enter the semaphore.»
Además:
«The Semaphore class does not enforce thread identity on calls to WaitOne or Release. It is the programmer's responsibility to ensure that threads do not release the semaphore too many times...»
Para comprender mejor el uso de la clase Semaphore procedamos a escribir el siguiente ejemplo.

Ejemplo de uso:

Archivo C# UsoSincronizacionSemaphore.cs [enlace alternativo]:

En la línea 9 definimos una variable de la clase Semaphore a la que le asociaremos una instancia más adelante. La variable entera _intervalo la usaremos para la especificación de un intervalo incremental entre cada ejecución de cada thread que pasará por el semáforo. En el método Main suceden las siguientes acciones:
  • Línea 20: Creación de una instancia de Semaphore con los siguientes valores para los argumentos:

    0: cantidad inicial de solicitudes que se pueden atender de forma concurrente.
    3: cantidad máxima de solicitudes sobre el semáforo.
  • Líneas 23-29: ciclo for para la creación de hasta cinco (5) threads, y su respectiva ejecución.
En el método Tarea:
  • Línea 48: Bloqueo del semáforo por un thread.
  • Línea 51: Incremento del intervalo de espera entre cada thread. El método Interlocked.Add [8] suma dos números enteros, y asegura esta operación sobre una región crítica.
  • Línea 58: Simulación de la ejecución de una tarea larga.
  • Líneas 61-62: Liberación del semáforo con la invocación del método Release.
Compilación:


  1. csc /target:exe UsoSincronizacionSemaphore.cs

Ejecución assembly:


  1. .\UsoSincronizacionSemaphore.exe

> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local):
Ejecución assembly UsoSincronizacionSemaphore.exe
Figura 1. Ejecución assembly UsoSincronizacionSemaphore.exe.

4. Práctica: Código C#

El siguiente ejemplo es análogo con el presentando en la receta Sincronización de Múltiples Threads usando Mutex, en este ejemplo usaremos, por supuesto, la clase Semaphore.


Compilación:


  1. csc /target:exe SincronizacionConsola.cs

Ejecución assembly:


  1. .\SincronizacionConsola.exe

> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local):
Ejecución assembly SincronizacionConsola.exe
Figura 2. Ejecución assembly SincronizacionConsola.exe.

5. Artefactos

Enlaces de descarga de los artefactos producidos durante el desarrollo de este artículo:

6. Conclusiones

Hemos aprendido a utilizar la clase Semaphore para la sincronización de múltiples threads en ejecución. Destacamos que este mecanismo de sincronización mantiene un contador sobre los threads que se hayan en la región crítica. Además, debemos tener en cuenta que la utilidad de un semáforo consiste en el control y monitoreo de piezas (threads) necesarias para la ejecución de un determinada tarea en una región crítica. La próxima receta C# la dedicaremos a sincronizar el acceso a una región de datos compartida.

Glosario

  • BCL
  • Multithreading
  • Región crítica
  • Sincronización
  • Thread

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]: Semaphore Class (System.Threading) - http://msdn.microsoft.com/en-us/library/system.threading.semaphore%28v=vs.110%29.aspx
[3]: Semaphore (programming) - Wikipedia, the free encyclopedia - https://en.wikipedia.org/wiki/Semaphore_(programming)
[4]: Receta C# No. 4-9: Sincronización de Múltiples Threads usando Mutex | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/07/receta-csharp-no-4-9-sincronizacion-de-multiples-threads-usando-mutex.html
[5]: Receta C# No. 4-7: Sincronizar la Ejecución de Múltiples Threads usando un Monitor | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/07/receta-csharp-no-4-7-sincronizar-la-ejecucion-de-multiples-threads-usando-un-monitor.html
[6]: Receta C# No. 4-8: Sincronizar la Ejecución de Múltiples Threads usando un Evento | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/07/receta-c-no-4-8-sincronizar-la-ejecucion-de-multiples-threads-usando-un-evento.html
[7]: WaitHandle Class (System.Threading) - http://msdn.microsoft.com/en-us/library/system.threading.waithandle%28v=vs.110%29.aspx
[8]: Interlocked.Add Method (Int32, Int32) (System.Threading) - http://msdn.microsoft.com/en-us/library/33821kfh%28v=vs.110%29.aspx


J

No hay comentarios:

Publicar un comentario

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