martes, 13 de octubre de 2015

Implementación de Semáforos y Barreras en Python.

Barreras

Los locks, también llamados mutex son objetos con dos estados posibles: adquirido o libre. Cuando un thread adquiere la barrera, los demás threads que pidan adquirirlo se bloquearán hasta que el thread que lo ha adquirido libere la barrera, momento en el cual podrá entrar otro thread.

La barrera se representa mediante la clase Lock. Llamando a Lock.acquire() el hilo bloqueará el Lock, de forma que el siguiente hilo que llame a Lock.acquire() se quedará a la espera de que el Lock se desbloquee. La llamada a Lock.release() desbloquea el Lock, haciendo que el hilo que estaba en espera continúe. Un ejemplo se muestra a continuación:
from threading import Thread, Lock
import time

class MiHilo(Thread):

    
    def __init__ (self, inicio, fin, bloqueo):
        Thread.__init__(self)
        self.inicio = inicio
        self.fin = fin
        self.bloqueo = bloqueo
      
    def run (self):
        bloqueo.acquire()
        for i in range(self.inicio,self.fin):
           print ("contador = "+str(i))
           time.sleep(0.2)
        bloqueo.release()
            
if __name__ == '__main__':
    bloqueo = Lock()
    bloqueo.acquire()
    hilo = MiHilo(0,10, bloqueo)
    hilo.start()
    time.sleep(1)
    for i in range (10,20):
       print ("main = "+str(i))
       time.sleep(0.1)
       
    bloqueo.release()

Esta clase Lock es muy simple. Cualquier hilo, puede liberar a otro sin importar si lo haya bloquedado o no, además si un hilo llama él mismo dos veces a acquire(), se queda bloqueado en la segunda llamada. Una mejor opción es RLock. En esta barrera solo el que haya llamado al método acquire() puede liberarlo con el método release() y un mismo hilo puede llamar varias veces a acquire() sin quedarse bloqueado, pero tiene que hacer el mismo número de llamadas a release() para desbloquearlo. El ejemplo siguiente muestra su funcionamiento:

from threading import Thread, RLock
import time

class MiHilo(Thread):
    def __init__ (self, inicio, fin, bloqueo):
        Thread.__init__(self)
        self.inicio = inicio
        self.fin = fin
        self.bloqueo = bloqueo

    def run (self):
        bloqueo.acquire()
        for i in range(self.inicio,self.fin):
           print ("contador = "+str(i))
           time.sleep(0.2)
        bloqueo.release()
            
if __name__ == '__main__':
    bloqueo = RLock()
    bloqueo.acquire()
    
    hilo = MiHilo(0,10, bloqueo)
    hilo.start()
    time.sleep(1)
    for i in range (10,20):
       print ("main = "+str(i))
       bloqueo.acquire()
       time.sleep(0.1)
       
    bloqueo.release()
    print ("El hilo todavia no ha comenzado")
    for i in range(10,20):
       bloqueo.release()

Semáforos

Un semáforo permite acceder a un determinado recurso a un número máximo de hilos simultáneamente. Si hay más hilos que el máximo permitido, los pone en espera y los va dejando pasar según van terminando los que están activos. Un semáforo actúa como un contador con un valor inicial. Cada vez que un hilo llama a Semaphore.acquire(), el contador se decrementa en 1 y se deja pasar al hilo. En el momento que el contador se hace cero, NO se deja pasar al siguiente hilo que llame a acquire(), sino que lo deja bloqueado. Cada vez que se llama a Semaphore.release(), el contador se incrementa en 1. Si se hace igual a cero, libera al siguiente hilo en la cola de espera. El ejemplo de su funcionamiento se muestra a continuación: 
from threading import Thread, Semaphore
import time, random


class MiHilo(Thread):
    def __init__(self, numero_hilo, semaforo):
        Thread.__init__(self)
        self.semaforo=semaforo
        self.numero_hilo = numero_hilo
        
    def run(self):
        semaforo.acquire()
        print ("Entra hilo "+str(self.numero_hilo))
        time.sleep(random.randrange(1,10,1))
        print ("Fin hilo " + str(self.numero_hilo))
        semaforo.release()          
        
if __name__ == '__main__':
    random.seed()
    semaforo = Semaphore(5)
    for i in range(0,10):
       hilo=MiHilo(i,semaforo)
       hilo.start()
       print ("Arrancado hilo "+str(i))
La información y los ejemplos se obtuvieron de las siguientes fuentes:
Los ejemplos se pueden descargar del siguiente link.

No hay comentarios.:

Publicar un comentario