queue.SimpleQueue es una cola FIFO segura para comunicar datos y tareas entre hilos. Está pensada para situaciones en las que no necesitas limitar la capacidad, registrar la finalización de cada tarea ni administrar locks manualmente. Su API es pequeña: los productores insertan objetos con put() y los consumidores los retiran con get().
Esta estructura resulta útil en workers, procesamiento en segundo plano, pipelines, registro asíncrono de eventos y comunicación entre componentes concurrentes dentro de un mismo proceso de Python.
Qué significa FIFO
FIFO significa first in, first out: el primer elemento que entra es el primero que sale.
from queue import SimpleQueue
cola = SimpleQueue()
cola.put("primero")
cola.put("segundo")
print(cola.get())
print(cola.get())
El resultado será primero y después segundo. Además de preservar el orden, la cola sincroniza el acceso cuando varios hilos la usan al mismo tiempo.
SimpleQueue frente a Queue
queue.Queue ofrece capacidad máxima, task_done() y join(). SimpleQueue no tiene límite y no registra si las tareas retiradas ya terminaron. Su menor cantidad de funciones es una ventaja cuando solo necesitas transportar objetos de forma segura.
Elige Queue cuando necesites backpressure, memoria limitada o seguimiento de tareas. Elige SimpleQueue cuando solo quieras una comunicación directa y thread-safe.
Patrón productor-consumidor
Un diseño común utiliza un hilo para producir trabajo y otro para procesarlo.
from queue import SimpleQueue
from threading import Thread
cola = SimpleQueue()
def productor():
for numero in range(5):
cola.put(numero)
cola.put(None)
def consumidor():
while True:
item = cola.get()
if item is None:
break
print(item * 2)
Thread(target=productor).start()
Thread(target=consumidor).start()
En este ejemplo, None funciona como centinela para indicar que no llegarán más elementos. En aplicaciones reales conviene usar un objeto único, porque None podría ser un dato válido.
FIN = object()
Comportamiento bloqueante
Por defecto, get() espera hasta que exista un elemento. Esto evita bucles que consultan la cola continuamente y consumen CPU sin necesidad.
También puedes usar get_nowait(). Si la cola está vacía, Python lanza queue.Empty.
from queue import Empty, SimpleQueue
cola = SimpleQueue()
try:
item = cola.get_nowait()
except Empty:
print("no hay elementos disponibles")
No uses empty para sincronizar
empty() representa solo una observación momentánea. Otro hilo puede insertar o retirar un elemento inmediatamente después. Por eso, no garantiza que el próximo get() no se bloquee.
Cuando uses acceso no bloqueante, captura Empty. En concurrencia, el estado puede cambiar entre dos instrucciones.
Hilos, no procesos
SimpleQueue está diseñada para hilos del mismo proceso. No sustituye a multiprocessing.Queue cuando productores y consumidores pertenecen a procesos diferentes.
Para revisar conceptos relacionados con control de ejecución, consulta la guía de asyncio.Runner en Python. Los workers que manejan recursos también necesitan una estrategia de limpieza, como se explica en weakref.finalize en Python.
Procesamiento de eventos
Una cola permite separar la creación de un evento de su procesamiento lento.
from queue import SimpleQueue
from threading import Thread
EVENTOS = SimpleQueue()
FIN = object()
def worker_eventos():
while True:
evento = EVENTOS.get()
if evento is FIN:
return
print(f"evento: {evento}")
worker = Thread(target=worker_eventos)
worker.start()
EVENTOS.put({"tipo": "login", "usuario": 42})
EVENTOS.put(FIN)
worker.join()
El productor puede continuar rápidamente, mientras el consumidor valida, agrupa, guarda o transmite los eventos.
Excepciones en el consumidor
Una excepción no controlada puede terminar el worker y dejar elementos pendientes. Envuelve el procesamiento en un bloque seguro.
def consumidor_seguro():
while True:
item = cola.get()
if item is FIN:
break
try:
procesar(item)
except Exception as error:
registrar_fallo(item, error)
Decide si los elementos fallidos se descartan, se reintentan o se envían a una cola de errores. Los reintentos ilimitados pueden crear un bucle infinito, por lo que conviene guardar el número de intentos.
Memoria y backpressure
SimpleQueue no tiene capacidad máxima. Si los productores generan datos más rápido que los consumidores, el uso de memoria puede crecer de forma continua. Esta es su principal limitación operativa.
Usa queue.Queue(maxsize=N) cuando el volumen sea imprevisible. El límite hace que los productores esperen y crea backpressure. Si puedes procesar elementos en grupos, la guía sobre itertools.batched en Python muestra cómo reducir operaciones repetidas.
Varios consumidores
Varios hilos pueden ejecutar get() sobre la misma cola. Cada elemento será retirado por un solo consumidor. La distribución exacta depende del planificador, por lo que no debes asumir un reparto round-robin.
workers = [Thread(target=consumidor_seguro) for _ in range(4)]
for worker in workers:
worker.start()
Para detenerlos, inserta un centinela por cada worker. Un único centinela solo detiene al hilo que lo recibe.
Objetos mutables
La cola transporta referencias, no copias. Si un productor modifica una lista o un diccionario después de insertarlo, el consumidor puede observar un estado inesperado. Prefiere objetos inmutables, copias o snapshots creados antes de llamar a put().
Cuándo no usar SimpleQueue
No la uses para comunicar máquinas diferentes, persistir mensajes, sobrevivir a reinicios o garantizar entrega después de una caída. Para esos requisitos utiliza un broker o stream durable como RabbitMQ, Redis Streams, Kafka o un servicio administrado.
Tampoco reemplaza a asyncio.Queue. Ejecutar un get() bloqueante en el hilo del event loop puede detener toda la aplicación asíncrona.
Pruebas
Prueba el orden FIFO, el cierre con centinelas, la producción concurrente, las excepciones y la terminación limpia. Evita depender solo de sleep(), porque produce pruebas lentas e inestables. Prefiere eventos, join() y timeouts controlados.
La guía de StrEnum en Python muestra una forma clara de representar estados cerrados de un worker, como esperando, ejecutando, fallido y detenido.
Monitorización
Como la cola no tiene límite, monitoriza la velocidad de producción, la velocidad de consumo, la latencia, los fallos de workers y la memoria. qsize() puede servir como métrica aproximada, pero no debe decidir la corrección del programa porque el valor puede cambiar inmediatamente.
Buenas prácticas
Usa un centinela único, documenta quién controla el cierre, captura errores, termina los hilos con join(), evita modificar objetos ya insertados y utiliza Queue(maxsize) cuando sea necesario controlar la memoria. Mantén explícita la política de reintentos y de elementos fallidos.
Referencias
Consulta la documentación oficial de queue.SimpleQueue y la documentación oficial de threading.
Conclusión
queue.SimpleQueue es una solución enfocada para comunicación FIFO entre hilos. Se ocupa de la sincronización y ofrece una interfaz pequeña. Funciona especialmente bien cuando no necesitas límite ni seguimiento de finalización. Su naturaleza ilimitada es el punto crítico: mide el equilibrio entre productores y consumidores y cambia a Queue(maxsize) cuando necesites backpressure.







