Python R2


Sincronización de hilos en python


Para poder sincronizar el acceso de nuestros hilos, en modulo de threading nos facilita estos métodos de sincronización : mutex, locks re-entrantes, semáforos, condiciones y eventos.

Mutex o locks o Candados

Realmente los mutex son la herramienta más fácil que hay de sintonización. Un mutex se usa en programación concurrente para que un solo procesos o hilo en nuestro caso pueda tomar un recurso compartido por otros. La parte de código que toma dicho recurso se llama sección crítica. El mutex solo puede tomar dos estados que son tomados y liberado. Miremos un ejemplo para aclarar las ideas.

En el archivo mutex.py y compare su salida con el de hilo.py de la entrada anterior

#!/usr/bin/python
# Nombre de Fichero : mutex.py

import threading
from time import sleep
mutex = threading.Lock()
class Hilo(threading.Thread):
    def __init__(self, id):
        threading.Thread.__init__(self)
        self.id = id

    def run(self):
        mutex.acquire()
        sleep(3-self.id)
        print "Yo soy %s la variable d tiene el valor %s"%(self.id,d)
        mutex.release()

d=1;
hilos = [Hilo(1),
Hilo(2),
Hilo(3)]

for h in hilos:
    h.start()

Bueno veremos otro ejemplito llamado mutex1.py

#!/usr/bin/python
# Nombre de Fichero : mutex.py

import threading
from time import sleep
mutex = threading.Lock()

n=0
class Hilo(threading.Thread):
    def __init__(self, id):
        threading.Thread.__init__(self)
        self.id = id

    def run(self):
        mutex.acquire()
        sleep(3-self.id)
        d.append(self.id)
        mutex.release()

d=[];
hilos = [Hilo(1),
Hilo(2),
Hilo(3)]

for h in hilos:
    h.start()

mutex.acquire()
print d
mutex.release()

comparemos la salida con mutex.acquire() y sin él. Recorrar que por cada mutex.acquire() que sacamos tenderemos que sacar tambien el mutex.release()
sino se nos quedara bloqueado el hilo de ejecución. Igual mente me parece que esta buene que vean como se bloquea al sacar el release.

locks re-entrantes – Rlook
La clase RLock es similar a Lock, pero puede ser adquirido por el mismo thread varias veces, y no quedará liberado hasta que el thread lo libere tantas veces como llamó a acquire.

Los semáforos

Los semáforos son parecidos a los mutex pero en vez de tomar el valor 1 y 0, toman n valores que nos indicara la cantidad de hilos, que puede tomar el recurso concurrentemente. Para esto python nos facilita la clase Semaphore y BoundedSemaphore. La diferencia de Semaphore y BoundedSemaphore es que, cuando se libera el recurso más veces que el n inicial del semáforo en Semaphore cambia dicha cota, mientras que en BoundedSemaphore lo considera un error de ValueError

Miremos este ejemplo llamado semaphore.py

#!/usr/bin/python
# Nombre de Fichero : semaphore.py

import threading
from time import sleep
semaforo = threading.Semaphore(2)

n=0
class Hilo(threading.Thread):
    def __init__(self, id):
        threading.Thread.__init__(self)
        self.id = id

    def run(self):
        semaforo.acquire()
        sleep(3-self.id)
        d.append(self.id)
        semaforo.release()

d=[];
hilos = [Hilo(1),
Hilo(2),
Hilo(3)]

for h in hilos:
    h.start()

sleep(4)
semaforo.acquire()
print d
semaforo.release()

fijese que si agrega justo antes del sleep(4) 3 release como estos

semaforo.release()
semaforo.release()
semaforo.release()

La salida cambia de orden ya que entra mas de uno hilo concurrente.
En cambio si después de agregar estas tres lineas, cambiamos la linea
semaforo = threading.Semaphore(2) por semaforo = threading.BoundedSemaphore(2)
dara error de tipo ValueError.

Las condiciones

Las condiciones son de utilidad para hacer que los threads sólo puedan entrar en la sección crítica de darse una cierta condición o evento.La clase condition tiene métodos wait, notify y notifyAll.

El método wait debe llamarse después de haber adquirido el objeto condiction con acquire. Este método libera el candado y bloquea al hilo hasta que una llamada a notify o notifyAll en otro hilo le indican que se ha cumplido la condición. El hilo que informa a los demás de que se ha producido la condición, también debe llamar a acquire antes de llamar a notify o notifyAll.

Espero que el ejemplo del archivo condition.py sea ilustrativo.

#!/usr/bin/python
# Nombre de Fichero : condition.py
import threading

cond = threading.Condition()

class Cliente (threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        while True:
            cond.acquire()
            cond.wait()
            mesa.pop()
            cond.notify()
            cond.release()

class Cosinero(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        while True:
            cond.acquire()
            if len(mesa) != 0: cond.wait()
            mesa.append("Torta de frutillas")
            cond.notify()
            cond.release()

print "El bar"

mesa = []
cliente = Cliente()
cosinero = Cosinero()

cliente.start()
cosinero.start()

while True:
    print mesa

acquire no bloqueante

Para que acquire no bloque el hilo le tenemos que pasar (False) como parámetro. Dicha invocación nos dará el resultado de si obtuvo el hilo o no. Modificaremos el ejemplo del mutex para que se vea lo que pasa.

#!/usr/bin/python
# Nombre de Fichero : mutex2.py

import threading
from time import sleep
mutex = threading.Lock()
class Hilo(threading.Thread):
    def __init__(self, id):
        threading.Thread.__init__(self)
        self.id = id

    def run(self):
        b = mutex.acquire(False)
        print b
        sleep(3-self.id)
        print "Yo soy %s la variable d tiene el valor %s"%(self.id,d)
        if b :mutex.release()

d=1;
hilos = [Hilo(1),
Hilo(2),
Hilo(3)]

for h in hilos:
    h.start()

Los eventos

La clase Event es un wrapper por encima de Condition y sirven principalmente para coordinar threads mediante señales que indican los eventos que han ocurrido. Los no tienen los métodos acquire y release.
El hilo que debe esperar el evento se pondra en espera por medio de la llama al método wait y se bloquea, le podriamos pasar como parámetro un número en coma flotante indicando el número máximo de segundos a esperar. Otro hilo, cuando ocurre el evento, envía la señal a los hilos bloqueados a la espera de dicho evento utilizando el método set. Los hilos que estaban esperando se desbloquean una vez recibida la señal. La bandera que indica si se ha producido el evento se puede volver a setar a falso usando clear. Los eventos son similares a las condiciones.

Un ejemplo lo vemos en eventos.py que es una modificación de condition.py

#!/usr/bin/python
# Nombre de Fichero :  eventos.py
import threading

evento = threading.Event()

class Cliente (threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        while True:
            self.evento.wait()
            mesa.pop()

class Cosinero(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        while True:
            if len(mesa) != 0: cond.wait()
            mesa.append("Torta de frutillas")
            evento.set()

print "El bar"

mesa = []
cliente = Cliente()
cosinero = Cosinero()

cliente.start()
cosinero.start()

while True:
    print mesa
Anuncios

2 comentarios to 'Sincronización de hilos en python'

Subscribe to comments with RSS o TrackBack to 'Sincronización de hilos en python'.

  1. luisr said,

    hola, la verdad no entiendo mucho de los hilos, pero aún así estoy cacharreando un poco.

    tengo una duda:
    si tuviera una lista de 255 IPs y quisiera hacerle ping con hilos para ganar en tiempo de ejecución, como limito el numero de hilos a 10 a la vez?
    PD: esto me funciona siempre que no exceda a 300 hilos

    este es el cod:
    import os
    import re
    import time
    import sys
    from threading import Thread
    #from impacket import smb

    class testit(Thread):
    def __init__ (self,ip):
    Thread.__init__(self)
    self.ip = ip
    self.status = -1
    def run(self):
    pingaling = os.popen(“ping -q -c2 “+self.ip,”r”)
    while 1:
    line = pingaling.readline()
    if not line: break
    igot = re.findall(testit.lifeline,line)
    if igot:
    self.status = int(igot[0])

    testit.lifeline = re.compile(r”(\d) received”)
    report = (“No response”,”Partial Response”,”Alive”)

    print time.ctime()

    pinglist = []

    # Determinar las PC encendidas
    for host in range(1,255):
    ip = “10.36.30.”+str(host)
    current = testit(ip)
    pinglist.append(current)
    current.start()

    # Mostrar los IP activos una vez que finaliza el ping
    for pingle in pinglist:
    pingle.join()
    if report[pingle.status] == ‘Alive’:
    print “Status from “,pingle.ip,”is”,report[pingle.status]

    print time.ctime()

    Otra duda: ¿Cómo hago para obtener el resultado de los 10 primeros ping y realizar otra operación con ellos al mismo tiempo que se lanzan los 10 ping subsiguientes?

    Es decir hacerle ping a las 10 primeras direcciones, una vez que terminen obtener el resultado y procesarlo y mientra los proceso hacer otro 10 ping… y así sucesivamente.


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s


A %d blogueros les gusta esto: