domingo, 10 de junio de 2012

Cuando sincronizar con Semaphore en C#.


Continuando con los post acerca de la programación con hilos, el día de hoy quiero explicarles otro método de sincronización de hilos de los cuales ya hemos visto lock , ahora veremos el método Semaphore, el cual limita el numero de hilos que puede acceder a un recurso o un grupo  de recursos de manera concurrente.
Para explicar de una manera mas coloquial un semaphore lo podemos asimilar como a un cajero de banco , donde la fila de depositantes son los hilos, los cuales esperan a que el cajero termine de atender para continuar con los que están en fila, con esto podemos concluir que un semaphore tiene una capacidad y si la capacidad llega a su valor máximo los demás hilos deben esperar a que se desocupe el semaphore para que los pueda atender.
Semaphore_1Semaphore_2
Para instanciar un objeto se tipo Semaphore , no sobra decir que se requiere el espacio de nombres System.Threading,y se debe tener en cuenta que el constructor de Semaphore requiere de mínimo dos parámetros, el primero es el numero de espacios de los recursos disponibles cuando el objeto crea una instancia, el segundo parámetro es el numero máximo de espacios disponibles o capacidad total. Si desea reservar algunos espacios para la llamada a un hilo, esto se puede realizar con el primer parámetro, de tal forma que el primer parámetro es mas pequeño que el segundo.
Semaphore sem = new Semaphore(Recursos Disponibles, Recursos Maximos).
Por ejemplo si creamos la siguiente instancia:
Semaphore sem = new Semaphore(3, 3);

Esto para nuestro ejemplo del cajero quiere decir que un cajero podrá atender tres personas al mismo tiempo y en el momento que se desocupe de una de esas personas y existiesen mas personas en la fila , sencillamente podrá ingresar ya que al desocuparse el semaphore quedara (2,3) , y como tenemos otra persona al ingresar el semaphore quedara (3,3).


Después de Instanciar el objeto semáforo, simplemente se requiere realizar un llamado al método WaitOne(),  de tal forma que con el llamado a este método se limite la porción de código a la cual desea limitarse el numero de hilos especificados al instaciar el objeto semaphore.

Sem.WaitOne(); entra al semáforo y el semáforo se decrementa.

Cuando son finalizados los procesos , es necesario llamar el método Release(), para liberar el espacio de recursos disponibles. El conteo de un semáforo disminuye cada vez que el hilo ingresa al semaphore y se incrementa cada vez que sale el hilo del semaforo.

Sem.Release(); Libera el semáforo y el semáforo se incrementa;

Sem.Release(int n); Libera n entradas del semáforo.

Cuando el conteo de semáforo es cero, los otros subprocesos esperan hasta que sea liberado algún hilo del semáforo, Cuando todos los hilos se encuentran liberados del semaphore el valor del conteo es le máximo,

A continuacion presente un pequeño ejemplo muy comun usado para la explicaciones de las funcionalidad de Semaphore, y se basa en el ejemplo que hemos estado hablando acerca del cajero.

   1:  using System;
   2:  using System.Threading;
   3:  namespace SemaphoreThreading
   4:  {
   5:      class Program
   6:      {
   7:          static void Main(string[] args)
   8:          {
   9:          for (int i = 0; i < 10; i++)
  10:              {
  11:                  threads[i] = new Thread(C_sharpcorner);
  12:                  threads[i].Name = "thread_" + i;
  13:                  threads[i].Start();
  14:              }
  15:             Console.Read();
  16:   
  17:          }
  18:   
  19:         static Thread[] threads = new Thread[10];
  20:          static Semaphore sem = new Semaphore(3, 3);
  21:          static void C_sharpcorner()
  22:          {
  23:              Console.WriteLine("{0} en Fila", Thread.CurrentThread.Name);
  24:              sem.WaitOne();
  25:              Console.WriteLine("{0} en atencion", Thread.CurrentThread.Name);
  26:              Thread.Sleep(300);
  27:              Console.WriteLine("{0} Saliendo", Thread.CurrentThread.Name);
  28:              sem.Release();
  29:          }
  30:   
  31:      }
  32:  }



El resultado de la aplicacion es la siguiente:

image

Si revisamos el resultado del ejemplo, veremos que maximo se estan atendiendo 3 personas y a medida que van siendo atendidos , las demas personas siguen en atencion, pero teniendo en cuenta que la cantidad maxima que atiende el cajero de manera concurrente son 3.


DATOS IMPORTANTES:

No se garantiza que la ejecución de hilos con Semaphore tenga un orden puede ser LIFO como puede ser FIFO.

La clase Semaphore no garantiza la identidad del hilo sobre el llamado de los métodos WaitOne() o Release. Es responsabilidad del desarrollador asegurarse de que los hilos no se libere el semáforo demasiadas veces.

Por ejemplo, supongamos que un semáforo tiene un conteo máximo de dos, y el que el hilo que A y el hilo B, ambos entran al semaforo. Si un error de programación el Hilo B hace que se llame a Release dos veces, ambas llamadas son exitosas. El recuento en el semáforo está llena, y cuando finalmente el subproceso A llama a Release, un SemaphoreFullException se lanza.

Los semáforos son de dos tipos: semáforos locales y semáforos con nombre del sistema.

Si crea un objeto Semaphore mediante un constructor que acepta un nombre, este se asocia con un semáforo del sistema operativo del mismo nombre.

El nombrado de los semáforos es accesible a través del sistema operativo y puede ser usado para sincronizar actividades de los procesos.

Puede crear objetos multipleSemaphore que representan el mismo semáforo con nombre del sistema, y se puede utilizar el OpenExisting () para abrir una ya existente sistema de semáforo con nombre.

Un semáforo local sólo existe dentro de su proceso. Puede ser utilizado por cualquier hilo en su proceso que tiene una referencia al objeto semáforo local. Cada objeto es un semáforo semáforo local por separado.

Diferencias con Lock Y Semaphore:

Con Lock lo que se permite es que solamente un hilo pueda entrar a la parte de código bloqueada y el bloqueo no es compartido con cualquier otros procesos, Semaphore hace lo mismo que lock pero permite que un x numero de hilos pueda entrar a la porción de código de manera concurrente.

En el próximo post analizaremos el uso de Mutex, y espero este pequeño aporte pueda aclarar a ustedes una nueva forma de poder sincronizar el uso de hilos.

4 comentarios: