Programación Multihilo en Python: Técnicas Eficientes de Concurrencia
Ejecutar varios hilos es similar a ejecutar varios programas concurrentemente, pero con beneficios:
- Múltiples hilos en un proceso comparten el mismo espacio de datos con el hilo principal, facilitando compartir información y comunicarse.
- Los hilos son "procesos ligeros" con menos sobrecarga de memoria; son más baratos que procesos.
Un hilo tiene inicio, secuencia de ejecución y conclusión. Mantiene un puntero de instrucción que rastrea dónde está ejecutándose actualmente.
- Puede ser preempted (interrumpido).
- Puede ponerse en espera temporal (sleeping) mientras otros hilos ejecutan (yielding).
Iniciar un Nuevo Hilo (Módulo thread)
Para crear un hilo, usa el método del módulo thread (Python 2) o _thread (Python 3):
thread.start_new_thread(function, args[, kwargs])
Retorna inmediatamente y el hilo hijo comienza ejecutando function con args. Cuando la función retorna, el hilo termina.
args es una tupla de argumentos (usa tupla vacía para sin argumentos). kwargs es diccionario opcional de argumentos por palabra clave.
Ejemplo
#!/usr/bin/python
import thread
import time
def print_time(threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print("%s: %s" % (threadName, time.ctime(time.time())))
try:
thread.start_new_thread(print_time, ("Thread-1", 2))
thread.start_new_thread(print_time, ("Thread-2", 4))
except:
print("Error: unable to start thread")
while 1:
pass
El módulo thread es efectivo para bajo nivel pero limitado comparado con threading.
El Módulo threading
El módulo threading (Python 2.4+) proporciona soporte potente de alto nivel para hilos.
Expone métodos de thread y añade:
- threading.activeCount(): Número de objetos hilo activos.
- threading.currentThread(): Objeto hilo del llamador.
- threading.enumerate(): Lista de todos los objetos hilo activos.
Clase Thread con métodos:
- run(): Punto de entrada del hilo.
- start(): Inicia hilo llamando a run().
- join([time]): Espera a que el hilo termine.
- isAlive(): Verifica si hilo sigue ejecutándose.
- getName() / setName(): Obtener/establecer nombre del hilo.
Crear Hilo con Threading
Pasos:
- Define subclase de Thread.
- Sobrescribe __init__(self[, args]) para argumentos adicionales.
- Sobrescribe run(self[, args]) para implementar qué hace el hilo.
Crea instancia y llama start() (que invoca run()).
Ejemplo
#!/usr/bin/python
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
print_time(self.name, 5, self.counter)
print("Exiting " + self.name)
def print_time(threadName, counter, delay):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# Crear hilos
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Iniciar hilos
thread1.start()
thread2.start()
print("Exiting Main Thread")
Sincronización de Hilos
El módulo threading incluye mecanismo de bloqueo (lock) simple para sincronizar hilos.
- Lock(): Crea nuevo bloqueo.
- acquire(blocking): Fuerza ejecución sincrónica. Si blocking=0, retorna 0 si no puede adquirir, 1 si sí. Si blocking=1, bloquea y espera.
- release(): Libera el bloqueo.
Ejemplo
#!/usr/bin/python
import threading
import time
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
threadLock.acquire()
print_time(self.name, self.counter, 3)
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
threads.append(thread1)
threads.append(thread2)
for t in threads:
t.join()
print("Exiting Main Thread")
Cola de Prioridad Multihilo (Queue)
El módulo Queue (Python 2) / queue (Python 3) permite crear colas con capacidad específica.
Métodos de control:
- get(): Elimina y retorna elemento de la cola.
- put(): Añade elemento a la cola.
- qsize(): Número de elementos actuales.
- empty(): True si cola vacía.
- full(): True si cola llena.
Ejemplo Completo
#!/usr/bin/python
import Queue
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print("Starting " + self.name)
process_data(self.name, self.q)
print("Exiting " + self.name)
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1
# Crear hilos
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# Llenar cola
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# Esperar a que cola se vacíe
while not workQueue.empty():
pass
# Notificar salida
exitFlag = 1
# Esperar hilos
for t in threads:
t.join()
print("Exiting Main Thread")
Resultado: Los hilos procesan elementos de la cola concurrentemente con sincronización.
Nota: En Python 3, usa import queue y queue.Queue(). El módulo thread se renombró a _thread.
python
- Dominando el Ciclo de Rosca Fanuc G76: Guía Completa para Maquinistas CNC
- Formado de roscas vs. laminado de roscas: diferencias clave
- Dominando tipos de datos en Python: Guía práctica
- Dominando operadores en Python: Guía completa
- Dominando el bucle while en Python: Sintaxis, ejemplos y mejores prácticas
- Dominando la sentencia pass en Python: Guía práctica
- Dominando argumentos de funciones en Python: Posicionales, por palabra clave y por defecto
- Dominando Diccionarios en Python: Creación, Manipulación y Técnicas Avanzadas
- Dominando la Programación Orientada a Objetos en Python: Clases, Herencia, Encapsulación y Polimorfismo
- Programación de Redes en Python: Sockets y Protocolos Avanzados
- Programación de Extensiones en Python con C: Crea Módulos Nativos Rápidos