queue.ShutDown: cierra colas y workers con seguridad

Publicado el: 28/09/2026
Tempo de leitura: 6 minutos
Código Python para colas con hilos y gestión de queue.ShutDown

queue.ShutDown es la excepción que utiliza el módulo queue de Python para indicar que una cola fue cerrada y ya no puede aceptar ni entregar trabajo de la forma habitual. Este recurso resuelve un problema clásico en programas con hilos: cómo avisar a varios workers que el procesamiento terminó sin depender de valores centinela improvisados, como None, una cadena especial o un objeto colocado dentro del flujo de datos.

En esta guía aprenderás cómo funciona el cierre de una cola, cómo afecta a productores y consumidores, cuándo conviene usar un cierre gradual o inmediato, cómo evitar bloqueos con join() y task_done(), y cómo mantener compatibilidad con versiones anteriores de Python.

Por qué es difícil detener una cola

El módulo queue se usa con frecuencia para distribuir trabajo entre hilos. Uno o varios productores agregan tareas mediante put(), mientras que los consumidores llaman a get() y procesan cada elemento. El problema aparece al finalizar: un consumidor bloqueado en get() puede esperar indefinidamente si no llegará ningún trabajo adicional.

La solución tradicional consiste en agregar un valor centinela. Cada worker comprueba ese marcador y sale de su bucle. Sin embargo, el número de centinelas debe coincidir con el número de workers, el marcador no puede confundirse con datos reales y una cola de prioridad puede exigir que el centinela sea comparable con los demás elementos.

Qué significa queue.ShutDown

queue.ShutDown señala que una operación no puede continuar porque la cola entró en estado de cierre. Un productor puede recibirla desde put() después del cierre, y un consumidor puede recibirla desde get() cuando la cola ya no puede entregar trabajo.

import queue

cola = queue.Queue()
cola.shutdown()

try:
    cola.put("nueva tarea")
except queue.ShutDown:
    print("La cola está cerrada")

La mejora principal es que el cierre pasa a formar parte del ciclo de vida de la propia cola, en lugar de depender de una convención escondida entre los datos.

Cierre gradual

Con el comportamiento predeterminado, shutdown() impide nuevas inserciones, pero permite consumir los elementos que ya estaban en la cola. Los workers continúan llamando a get() hasta vaciarla. Después, las nuevas lecturas generan queue.ShutDown.

import queue
import threading

cola = queue.Queue()

def worker():
    while True:
        try:
            tarea = cola.get()
        except queue.ShutDown:
            break
        try:
            procesar(tarea)
        finally:
            cola.task_done()

hilos = [threading.Thread(target=worker) for _ in range(4)]
for hilo in hilos:
    hilo.start()

for tarea in cargar_tareas():
    cola.put(tarea)

cola.shutdown()
cola.join()
for hilo in hilos:
    hilo.join()

Este patrón es apropiado cuando todas las tareas enviadas deben terminar antes de cerrar la aplicación. El coordinador deja de producir, cierra la cola, espera las tareas pendientes y finalmente espera la terminación de los hilos.

Cierre inmediato

Algunos fallos requieren abandonar el trabajo pendiente. Por ejemplo, una dependencia crítica puede dejar de responder, el proceso puede tener un plazo estricto de apagado o continuar podría dañar datos. Para esos casos existe el cierre inmediato.

cola.shutdown(immediate=True)

Debe usarse con cuidado porque puede romper la expectativa habitual de que join() solo retorna después de que cada elemento haya recibido su correspondiente llamada a task_done(). No es una alternativa general al drenaje gradual.

Productores bloqueados

Una cola limitada puede bloquear productores cuando alcanza su capacidad máxima. Al iniciar el cierre, las llamadas bloqueadas a put() son liberadas y generan queue.ShutDown. Así se evita que un productor espere para siempre un espacio que nunca volverá a utilizarse.

def productor(cola, elementos):
    for elemento in elementos:
        try:
            cola.put(elemento)
        except queue.ShutDown:
            registrar_cancelacion(elemento)
            return

En un sistema bien diseñado, esta excepción puede representar un evento esperado del ciclo de vida y no necesariamente un error inesperado.

Estructura correcta del consumidor

El consumidor debe capturar queue.ShutDown alrededor de get(). La llamada a task_done() corresponde únicamente a un elemento recibido correctamente. No debe ejecutarse cuando get() lanzó la excepción.

def consumidor(cola):
    while True:
        try:
            elemento = cola.get()
        except queue.ShutDown:
            return
        try:
            ejecutar(elemento)
        except Exception:
            registrar_fallo(elemento)
        finally:
            cola.task_done()

Esta estructura también garantiza que una tarea fallida sea reconocida, evitando que join() espere indefinidamente.

Por qué es mejor que los centinelas

Los centinelas mezclan información de control con datos del negocio. Si None es un elemento válido, no puede representar el final. Varios workers suelen requerir varios marcadores. En una PriorityQueue, además, el centinela puede fallar si no puede compararse con los demás elementos.

El estado de cierre de la cola evita esas ambigüedades, bloquea nuevas inserciones y puede liberar tanto productores como consumidores. Los centinelas siguen siendo útiles para compatibilidad, pero el mecanismo nativo comunica mejor la intención.

Compatibilidad entre versiones

Antes de adoptar la API, confirma la versión mínima de Python del proyecto. Una biblioteca que también soporte intérpretes antiguos puede ocultar la diferencia mediante un adaptador.

def cerrar_cola(cola, centinela=None, workers=1):
    if hasattr(cola, "shutdown"):
        cola.shutdown()
        return
    for _ in range(workers):
        cola.put(centinela)

El fallback no tiene exactamente la misma semántica. Los centinelas no impiden futuras llamadas a put() y tampoco liberan automáticamente productores bloqueados en una cola llena. Estas limitaciones deben documentarse.

Pruebas recomendadas

Las pruebas deben cubrir al menos una cola vacía, una cola con tareas pendientes, un productor bloqueado por falta de espacio y el cierre inmediato. Usa timeouts para que una regresión de sincronización no congele toda la suite.

def test_put_despues_del_cierre():
    import queue
    cola = queue.Queue()
    cola.shutdown()
    try:
        cola.put_nowait(1)
    except queue.ShutDown:
        pass
    else:
        raise AssertionError("Se esperaba queue.ShutDown")

Las pruebas de integración también deben comprobar que todos los workers terminan, que el cierre gradual completa las tareas enviadas y que el cierre inmediato registra de forma clara los trabajos descartados.

Errores comunes

Un error frecuente es cerrar la cola mientras los productores siguen activos sin enseñarles a tratar queue.ShutDown. Otro es usar immediate=True y luego asumir que todas las tareas terminaron. También es incorrecto intentar reabrir la misma cola agregando nuevos elementos. El cierre debe considerarse definitivo para esa instancia.

Para reiniciar el pipeline, crea una nueva cola y un nuevo grupo de workers. Las generaciones explícitas son más fáciles de entender y probar que un intento de reiniciar estado compartido.

Recomendaciones de arquitectura

Asigna a un coordinador único la responsabilidad de cerrar la cola. Evita que cualquier worker pueda apagarla sin comunicarlo al resto del sistema. Registra métricas como tareas aceptadas, completadas, rechazadas, descartadas y tiempo de drenaje.

En servicios, combina el cierre de la cola con señales del sistema operativo y un plazo máximo de apagado gradual. Una política habitual consiste en dejar de aceptar nuevas solicitudes, cerrar la cola gradualmente, esperar un tiempo limitado y pasar al modo inmediato solo si vence el plazo.

Para ampliar el tema, consulta las guías de Academify sobre contextos y limpieza segura, intérpretes aislados, asyncio.Queue.shutdown y asyncio TaskGroup.

Las principales referencias externas son la documentación oficial de queue y la documentación oficial de threading.

Conclusión

queue.ShutDown vuelve explícito el ciclo de vida de las colas de trabajo con hilos. En lugar de insertar marcadores artificiales entre los datos, el coordinador cierra la cola, impide nuevos envíos, libera operaciones bloqueadas y permite que los workers reconozcan el final mediante una excepción específica. Usa cierre gradual cuando el trabajo pendiente deba completarse y reserva el modo inmediato para cancelaciones en las que abandonar tareas sea aceptable. Con un tratamiento disciplinado de get(), put(), task_done() y join(), la API reduce deadlocks y facilita el mantenimiento del pipeline.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código Python que representa filtros de valores None con operator.is_none
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    operator.is_none: filtra None en pipelines Python

    Aprende operator.is_none en Python para filtrar None sin eliminar cero, False o cadenas vacías.

    Ler mais

    Tempo de leitura: 5 minutos
    28/09/2026
    Entorno Linux que representa temporizadores con os.timerfd_create en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.timerfd_create: timers Linux precisos en Python

    Aprende os.timerfd_create en Python para timers Linux precisos, integración con poll, intervalos y limpieza segura.

    Ler mais

    Tempo de leitura: 5 minutos
    27/09/2026
    Entorno de desarrollo con varias pantallas que representa threads y el GIL en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sys._is_gil_enabled: comprueba si el GIL está activo

    Aprende a detectar si el GIL está activo en Python y adapta concurrencia, pruebas, métricas y compatibilidad con builds free-threaded.

    Ler mais

    Tempo de leitura: 5 minutos
    27/09/2026
    Terminal en un portátil representando cambios temporales de directorio con contextlib.chdir
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    contextlib.chdir: cambia directorios temporalmente

    Aprende contextlib.chdir en Python para cambiar directorios temporalmente con seguridad en scripts, pruebas, builds y automatizaciones.

    Ler mais

    Tempo de leitura: 5 minutos
    26/09/2026
    Desarrolladora usando intérpretes aislados de Python en un entorno de servidores
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    concurrent.interpreters: paralelismo aislado en Python

    Aprende concurrent.interpreters en Python para usar intérpretes aislados, tareas paralelas, colas, compatibilidad y cierre seguro.

    Ler mais

    Tempo de leitura: 7 minutos
    26/09/2026
    Desarrollador usando Python para inspeccionar archivos con pathlib.Path.info
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pathlib.Path.info: inspecciona archivos con eficiencia

    Aprende pathlib.Path.info en Python para inspeccionar archivos y directorios con eficiencia.

    Ler mais

    Tempo de leitura: 7 minutos
    25/09/2026