Índice
0. Introducción1. Problema
2. Solución
3. Discusión de la Solución
3.1 Propiedades de `SemaphoreSlim`
3.2 Constructores de `SemaphoreSlim`
4. Práctica: Código C#
5. Conclusiones
6. Glosario
7. Literatura & Enlaces
0. Introducción
En esta nueva receta multithreading en C# aprenderemos cómo limitar el acceso a un recurso compartido con la clase SemaphoreSlim. Ya veremos que esto nos puede resultar útil para ciertas restricciones que puedieran ser impuestas sobre un recurso compartido: archivo, base de datos, recurso de red, etc. El ejemplo nos ilustrará cómo podemos utilizar ese artefacto para simular el acceso concurrente a una base de datos. (Evidentemente este tipo de ejemplos será únicamente para ilustrar el concepto base de esta clase y su funcionalidad.)
1. Problema
Requerimos de una técnica programática en C# para limitar el número de threads que pueden acceder concurrentemente a un recurso de acceso compartido.
2. Solución
La biliblioteca base de clases (BCL) de .NET cuenta con la clase `SemaphoreSlim` para responder a el requerimiento previo.
3. Discusión de la Solución
La clase `SemaphoreSlim` reside en el espacio de nombres System.Threading. Esta clase básicamente constituye [1] una versión alternativa (lightweight) a la clase `Semaphore` [4, 3] que para el propósito solicitado permite limitar el número de threads (o directus et simplux, hilos) que tienen permitido acceder a un recurso compartido de forma concurrente [2].
3.1 Propiedades sobresalientes de `SemaphoreSlim`
La clase `Semaphore` cuenta con las propiedades `AvailableWaitHandle`, y `CurrentCount`:
Figura 1. Propiedades de SemaphoreSlim [2]. |
Como se describe en la Figura 1, la propiedad `AvailableWaitHandle` retorna el objeto `WaitHandle` que es requerido para realizar esperas sobre el semáforo. Mientras que la propiedad `CurrentCount` retorna el número de threads permitidos sobre el recurso compartido. Esta última propiedad se establece al instanciar una clase de `Semaphore`, dado que se requiere especificar la cantidad límite de threads que podrán acceder de forma concurrente al recurso compartido en cuestión.
3.2 Constructores de `SemaphoreSlim`
Descripción general de los constructores disponibles en `SemaphoreSlim` [2]:
Figura 2. Constructores de `SemaphoreSlim` [2]. |
3.2 Notas importantes de uso de `SemaphoreSlim`
Desde [1] nos advierten:
«There is no sense in using it -`SemaphoreSlim`-, except in one very important scenario, we can create a named semaphore like a named mutes and use it to synchronize threads in different programs. Semaphore does not use Windows Kernel semaphores and does not support interprocess synchronization, so use Semaphore in this case.
4. Práctica: Código C#
Pasemos a crear un ejemplo sencillo pero funcionalmente ilustrativo de uso de la clase `SemaphoreSlim` en el que vamos a simular el acceso a una base de datos (recurso compartido) por múltiples threads:
Archivo C# AccesoBaseDatosConSemaphoreSlim.cs [enlace alternativo]:
En las líneas 35-49 se declara el método `AccederBaseDatos`; este método lleva a cabo las siguientes operaciones:
- Linea 37: Muestra mensaje en la salida estándar (consola) anunciando el intento de acceso a la base de datos por parte del thread actual.
- Línea 38: Bloquea el thread actual hasta que tenga permiso de acceder a la base de datos.
- Línea 40: Anuncia el acceso recién permitido a la base de datos al thread.
- Línea 43: Simula tiempo de una operación transaccional o de consulta a la base de datos.
- Línea 45: Anuncia que el thread ha finalizado su ejecución.
- Línea 48: Libera el thread desde pool de threads.
Por otro parte, el método de inicio de ejecución de la aplicación, `Main` (líneas 12-31), se encarga de instanciar hasta 6 threads y ponerlos en ejecución sobre el método `AccederBaseDatos` (línea 24). Cada thread recibe un nombre y un tiempo de espera (tiempo simulado de ejecución de la operación transaccional o de consulta): líneas 20 y 22, respectivamente.
Compilación:
- csc /t:exe AccesoBaseDatosConSemaphoreSlim.cs
Ejecución assembly:
- .\AccesoBaseDatosConSemaphoreSlim.exe
5. Conclusiones
En esta receta multithreading hemos aprendido a entender y a usar la clase `SemaphoreSlim` del namespace System.Threading para controlar el límite de threads que pueden acceder de forma concurrente a un recurso compartido. En el ejemplo de la sección 4 se simuló el acceso concurrente a una base de datos por parte de 6 threads: sólo pueden acceder de manera simultánea 4 threads, los 2 restantes deberán esperar a que uno o más threads finalicen las operaciones transaccionales o de consulta especificadas y la liberación correspondiente. En la próxima receta multithreading aprederemos a usar la clase `AutoResetEvent` para el envío de notificaciones entre threads.
6. Glosario
- .NET
- BCL
- Hilo
- Multithreading
- Recurso compartido
- Thread
7. Literatura & Enlaces
[1]: Multithreading in C# 5.0 Cookbook by Eugene Agafonov. Copyright 2013 Eugene Agafonov, 978-1-84969-764-4.[2]: SemaphoreSlim Class (System.Threading) - https://msdn.microsoft.com/en-us/library/vstudio/system.threading.semaphoreslim(v=vs.100).aspx
[3]: Semaphore Class (System.Threading) - https://msdn.microsoft.com/en-us/library/vstudio/system.threading.semaphore(v=vs.100).aspx
[4]: OrtizOL - Experiencias Construcción Software (xCSw): Receta C# No. 4-10: Sincronización de Múltiples Threads usando un Semáforo - http://ortizol.blogspot.com/2014/07/receta-csharp-no-4-10-sincronizacion-de-multiples-threads-usando-un-semaforo.html
J