Aprende a crear y gestionar threads en Python: concurrencia vs paralelismo, ciclo de vida, y el problema de las race conditions.
threading.Threadimport threading class Trabajador(threading.Thread): def __init__(self, nombre, tarea): super().__init__() self.nombre = nombre self.tarea = tarea def run(self): # Este método se ejecuta cuando llamas start() print(f'{self.nombre} ejecutando: {self.tarea}') w1 = Trabajador('Worker-1', 'procesar imagen')w2 = Trabajador('Worker-2', 'enviar email') w1.start()w2.start()Regla clave: siempre llama t.start() para lanzar el thread. Nunca llames t.run() directamente — eso ejecuta la función en el hilo actual (sin crear un nuevo thread).
Por defecto, el programa espera que todos los threads terminen antes de salir. Un thread daemon se interrumpe automáticamente cuando el proceso principal termina — ideal para tareas de fondo que no deben bloquear la salida del programa.
import threading, time def monitor(): while True: print('Monitoreando...') time.sleep(1) # Con daemon=True: el thread muere con el programat = threading.Thread(target=monitor, daemon=True)t.start() time.sleep(3)print('Main termina — el daemon se interrumpe aquí')import threading def alerta(): print('¡Tiempo cumplido!') # Llama alerta() después de 5 segundost = threading.Timer(5.0, alerta)t.start() # Cancela si no ha disparado aúnt.cancel()El resultado cambia en cada ejecución porque depende de cuándo exactamente el OS interrumpe cada thread. Este es el sello de una race condition: mismo código, resultados distintos.
La solución a las race conditions es la sincronización: usar Lock, Event y otras primitivas del módulo threading. Esto se cubre en detalle en el módulo de Sincronización.