threading
El módulo threading ejecuta hilos del sistema operativo en un proceso con memoria compartida. Usa hilos para solapamiento de E/S; protege el estado mutable compartido con Lock, RLock, Event y Condition.
Receta
import threading
counter = 0
lock = threading.Lock()
def inc() -> None:
global counter
with lock:
counter += 1Cuándo usar esto:
- Bloqueo de E/S HTTP, DB, sistema de archivos en paralelo
- Trabajadores en segundo plano en una aplicación síncrona
- Temporizadores y tareas periódicas (
threading.Timer) - Señalización entre hilos (
Event) - Puente entre bibliotecas síncronas y asíncronas a través de un ejecutor
Ejemplo de trabajo
import threading
import time
from queue import Queue
results: Queue[int] = Queue()
stop = threading.Event()
def worker(worker_id: int) -> None:
while not stop.is_set():
time.sleep(0.05)
results.put(worker_id)
threads = [threading.Thread(target=worker, args=(i,), daemon=True) for i in range(3)]
for t in threads:
t.start()
time.sleep(0.2)
stop.set()
for t in threads:
t.join(timeout=1)
print("collected", results.qsize())Lo que esto demuestra:
Eventseñala un cierre ordenado- Los hilos demonio se cierran cuando el principal termina; usa hilos no demonio para trabajo de vaciado
Queuepasa resultados sin bloqueo manual de una listajoin(timeout)evita que el cierre se cuelgue
Inmersión Profunda
Primitivas
| Tipo | Uso |
|---|---|
| Lock | Exclusión mutua |
| RLock | Bloqueo reentrante del mismo hilo |
| Event | Bandera persistente o de un solo uso |
| Semaphore | Limita el recuento de concurrencia |
Seguridad de Hilos
- Prefiere mensajes inmutables a través de colas
- Protege invariantes que abarcan múltiples operaciones con bloqueos
threading.local()para estado por hilo- Interbloqueo (Deadlock): adquiere siempre los bloqueos en un orden consistente
Errores Comunes
- Mutaciones compartidas sin bloqueo - las condiciones de carrera corrompen los datos. Solución:
with lock:oqueue. - Interbloqueo con bloqueos anidados - A luego B vs B luego A. Solución: orden de bloqueo.
- Hilos demonio a mitad de escritura - salida truncada al salir. Solución:
joinde trabajadores no demonio. - Hilos limitados por CPU para velocidad - GIL limita el bytecode paralelo. Solución:
multiprocessing. - Demasiados hilos - cambio de contexto caótico. Solución: tamaño del pool ~ esperas de E/S concurrentes.
Alternativas
| Alternativa | Úsalo cuando | No lo uses cuando |
|---|---|---|
| asyncio | E/S de alta dispersión, APIs asíncronas | Solo SDK de bloqueo |
| multiprocessing | Paralelismo de CPU | Necesitas memoria compartida |
| concurrent.futures | Abstracción de pool | Ciclo de vida de hilo personalizado |
Preguntas Frecuentes
¿Cuántos hilos?
Empieza cerca de las operaciones de bloqueo concurrentes (conexiones), no del recuento de núcleos de CPU.
¿Lock vs RLock?
RLock cuando el mismo hilo vuelve a entrar en código protegido (ayudantes recursivos).
¿Es `list.append` seguro para hilos?
Un solo append es atómico en CPython, pero la lectura-modificación-escritura compuesta no lo es.
¿threading vs _thread?
Usa threading - nivel más alto, soporta bloqueos y join.
¿Cómo interactúan los hilos con asyncio?
loop.run_in_executor ejecuta código de bloqueo en un pool de hilos sin bloquear el bucle.
¿Qué es el almacenamiento local de hilos?
Los atributos de threading.local() están aislados por hilo; útil para el contexto de solicitud en WSGI síncrono.
¿Puedo compartir SQLite entre hilos?
Usa la misma conexión por hilo o comprueba el modo de seguridad de hilos de sqlite3; a menudo acceso serial.
¿Cómo nombro los hilos?
Thread(name="worker-1") mejora la legibilidad del depurador y los registros.
¿Se reutilizan los hilos de ThreadPoolExecutor?
Sí, el pool amortiza el costo de creación de hilos.
¿Cómo pruebo el código de threading?
Pruebas de estrés + threading.Barrier; evita aserciones de tiempo inestables; usa eventos.
Relacionado
- El GIL y Python con Hilos Libres - Límites de CPU
- Colas y Productor/Consumidor - Transferencia segura
- concurrent.futures - Pools
- Puente Síncrono/Asíncrono - ejecutores
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.