queue.SimpleQueue é uma fila FIFO segura para uso entre threads. Ela foi criada para cenários em que você precisa transferir tarefas ou dados de um produtor para um consumidor sem configurar limite de capacidade, sem controlar manualmente locks e sem usar a estrutura mais completa de queue.Queue.
Na prática, SimpleQueue é útil em pipelines, workers, processamento em segundo plano, coleta de eventos, logging assíncrono e comunicação entre partes concorrentes de uma aplicação. Sua API é pequena: você adiciona itens com put() e remove com get().
O que é uma fila FIFO
FIFO significa first in, first out: o primeiro item inserido é o primeiro a sair. Esse comportamento combina bem com tarefas que precisam preservar ordem de chegada.
from queue import SimpleQueue
fila = SimpleQueue()
fila.put("primeiro")
fila.put("segundo")
print(fila.get())
print(fila.get())
O resultado será primeiro e depois segundo. A fila organiza a ordem e também sincroniza o acesso quando várias threads a utilizam.
SimpleQueue versus Queue
queue.Queue oferece recursos adicionais como capacidade máxima, task_done() e join(). Já SimpleQueue é não limitada e não acompanha a conclusão das tarefas. Isso reduz a API e pode diminuir o custo operacional quando esses controles não são necessários.
Escolha Queue quando precisar de backpressure, limite de memória ou coordenação explícita entre tarefas produzidas e concluídas. Escolha SimpleQueue quando precisar apenas de uma passagem thread-safe e direta de objetos.
Produtor e consumidor
Um padrão clássico usa uma thread para produzir dados e outra para consumir.
from queue import SimpleQueue
from threading import Thread
fila = SimpleQueue()
def produtor():
for numero in range(5):
fila.put(numero)
fila.put(None)
def consumidor():
while True:
item = fila.get()
if item is None:
break
print(item * 2)
Thread(target=produtor).start()
Thread(target=consumidor).start()
O valor None funciona como sentinela, indicando que não haverá novos itens. Em sistemas reais, prefira um objeto sentinela exclusivo para evitar conflito com dados válidos.
FIM = object()
Bloqueio no get
Por padrão, get() bloqueia até que um item esteja disponível. Esse comportamento evita loops consumindo CPU para verificar repetidamente se a fila está vazia.
Também é possível usar get_nowait(). Quando não há item, a função lança queue.Empty.
from queue import Empty, SimpleQueue
fila = SimpleQueue()
try:
item = fila.get_nowait()
except Empty:
print("nenhum item disponível")
O modo não bloqueante é útil quando a thread precisa continuar executando outras atividades.
Não confie em empty para sincronização
O método empty() informa um estado momentâneo. Outra thread pode inserir ou remover um item imediatamente após a verificação. Portanto, não use empty() como garantia de que um get() seguinte não bloqueará.
if not fila.empty():
item = fila.get_nowait()
Mesmo nesse exemplo, ainda é mais seguro tratar Empty. Em concorrência, condições podem mudar entre duas linhas de código.
Filas e threading
SimpleQueue foi projetada para threads do mesmo processo. Ela não substitui multiprocessing.Queue quando produtores e consumidores estão em processos diferentes.
Para entender melhor threads, veja o artigo sobre execução assíncrona e controle de fluxo no Python. Também vale revisar limpeza segura de recursos com weakref.finalize, pois workers reais frequentemente precisam encerrar arquivos, conexões e sessões.
Processamento de eventos
Uma fila simples pode separar a geração de eventos do trabalho mais pesado.
from queue import SimpleQueue
from threading import Thread
EVENTOS = SimpleQueue()
FIM = object()
def registrar_eventos():
while True:
evento = EVENTOS.get()
if evento is FIM:
return
print(f"evento: {evento}")
worker = Thread(target=registrar_eventos)
worker.start()
EVENTOS.put({"tipo": "login", "usuario": 42})
EVENTOS.put(FIM)
worker.join()
Esse desenho reduz o tempo gasto pela thread que cria o evento. O consumidor pode persistir, agrupar ou transmitir informações.
Tratamento de exceções no consumidor
Uma exceção não tratada pode encerrar o worker e deixar itens acumulados. Envolva o processamento em try e registre falhas.
def consumidor_seguro():
while True:
item = fila.get()
if item is FIM:
break
try:
processar(item)
except Exception as erro:
registrar_erro(item, erro)
Defina também se itens com falha serão descartados, reenfileirados ou enviados para uma fila de erros. Reenfileirar sem limite pode criar repetição infinita.
Controle de memória
Como SimpleQueue não possui capacidade máxima, um produtor muito mais rápido que o consumidor pode aumentar o uso de memória. Esse é o principal cuidado da estrutura.
Se o volume for imprevisível, use queue.Queue(maxsize=N). O limite faz o produtor esperar e cria backpressure. Para processamento em blocos, o guia sobre itertools.batched no Python mostra como agrupar itens e reduzir chamadas repetitivas.
Múltiplos consumidores
Várias threads podem chamar get() na mesma fila. Cada item será entregue a um consumidor. A distribuição exata depende do escalonamento e não deve ser usada para garantir afinidade de tarefa.
workers = [Thread(target=consumidor_seguro) for _ in range(4)]
for worker in workers:
worker.start()
Ao finalizar, envie uma sentinela para cada consumidor. Uma única sentinela encerra apenas a thread que a retirar.
Quando não usar
Não use SimpleQueue para comunicação entre máquinas, persistência durável, garantia de entrega após falha do processo ou filas que precisam sobreviver a reinicializações. Nesses casos, considere RabbitMQ, Redis Streams, Kafka ou um serviço gerenciado.
Também não use a estrutura como substituta automática de asyncio.Queue em código assíncrono. O método get() bloqueia a thread e pode travar o event loop.
Testes
Teste ordem FIFO, encerramento por sentinela, exceções e produção concorrente. Evite testes baseados apenas em sleep(), pois eles podem ficar instáveis. Prefira eventos, joins e timeouts controlados.
O artigo sobre StrEnum no Python mostra uma forma útil de representar estados fechados de workers, como aguardando, executando e finalizado.
Boas práticas
Use uma sentinela exclusiva, documente quem encerra a fila, trate exceções no consumidor, monitore crescimento, finalize threads com join() e escolha Queue quando precisar de limite ou acompanhamento de tarefas. Mantenha os objetos da fila pequenos e evite compartilhar estruturas mutáveis que ainda serão alteradas por outra thread.
Referências
Consulte a documentação oficial de queue.SimpleQueue e a documentação do módulo threading.
Conclusão
queue.SimpleQueue é uma solução direta para comunicação FIFO entre threads. Ela cuida da sincronização, mantém uma API pequena e funciona bem quando não há necessidade de limite de capacidade ou rastreamento de conclusão. O ponto decisivo é compreender que a fila é ilimitada: monitore o ritmo entre produtores e consumidores e migre para Queue(maxsize) quando a pressão de memória precisar ser controlada.







