ProcessPoolExecutor kill_workers: encerre processos

Publicado em: 19/09/2026
Tempo de leitura: 6 minutos
Servidores representando workers paralelos do ProcessPoolExecutor

Aplicações Python que usam múltiplos processos normalmente encerram o pool de maneira cooperativa, esperando as tarefas terminarem e liberando os recursos com calma. Porém, há situações em que isso não basta: um worker pode travar, ficar preso em uma extensão nativa, consumir memória sem controle ou impedir o desligamento da aplicação. Para esses casos, versões recentes do Python adicionaram os métodos terminate_workers() e kill_workers() ao ProcessPoolExecutor. Eles permitem interromper os processos trabalhadores de forma explícita e previsível.

Neste guia, você aprenderá a diferença entre terminar e matar workers, quando usar cada opção, como tratar tarefas pendentes, quais exceções esperar e como estruturar um encerramento seguro sem deixar processos órfãos.

O problema de encerrar pools travados

O ProcessPoolExecutor facilita a execução paralela de funções em processos separados. Em condições normais, o bloco with chama shutdown() ao sair e aguarda o encerramento dos workers. Isso funciona bem quando as tarefas respondem, finalizam ou levantam exceções normalmente.

O problema aparece quando uma tarefa entra em loop infinito, trava em código externo ou fica bloqueada em uma operação que não retorna. Nesses casos, esperar indefinidamente não é aceitável para serviços, pipelines, filas ou rotinas de manutenção. O processo principal precisa de um mecanismo de emergência.

O tema complementa outros conteúdos da Academify sobre concorrência, como InterpreterPoolExecutor no Python, os.process_cpu_count no Python, asyncio.Queue.shutdown no Python e queue.SimpleQueue no Python.

terminate_workers e kill_workers

Os dois métodos encerram imediatamente os workers existentes e também executam internamente o processo de desligamento do executor. A diferença principal está no sinal usado pelo sistema operacional.

  • terminate_workers() chama o equivalente a Process.terminate().
  • kill_workers() chama o equivalente a Process.kill().

Em sistemas POSIX, terminar costuma enviar SIGTERM, enquanto matar normalmente envia SIGKILL. O primeiro oferece uma chance limitada de reação ao processo; o segundo força o encerramento e não pode ser tratado pelo worker. No Windows, os detalhes internos diferem, mas a ideia de uma interrupção mais forte continua válida.

from concurrent.futures import ProcessPoolExecutor

executor = ProcessPoolExecutor(max_workers=4)
try:
    futures = [executor.submit(processar, item) for item in itens]
    # processamento normal
except TimeoutError:
    executor.terminate_workers()

Depois de chamar qualquer um desses métodos, não envie novas tarefas ao executor. Ele deve ser considerado encerrado.

Quando usar terminate_workers

Prefira terminate_workers() como primeira etapa quando você precisa interromper o pool, mas ainda quer adotar a alternativa menos agressiva. Ele é apropriado quando os workers estão demorando demais, quando o serviço está entrando em desligamento ou quando um limite operacional foi excedido.

Mesmo assim, não dependa de blocos finally dentro do worker para garantir consistência. Um processo terminado pode não concluir gravações, buffers ou transações. Recursos externos, como banco de dados e arquivos, devem ser protegidos por transações, operações idempotentes e checkpoints.

Quando usar kill_workers

kill_workers() é a opção de último recurso. Use quando os processos não respondem à terminação, estão presos em código nativo ou precisam desaparecer imediatamente para proteger a máquina. Um exemplo seria um worker consumindo memória rapidamente ou impedindo a reinicialização de um serviço crítico.

executor.terminate_workers()

# Se a aplicação ainda detectar processos resistentes,
# um novo pool ou uma camada supervisora pode aplicar kill.

Na prática, a aplicação deve registrar por que a intervenção extrema ocorreu. Matar processos sem observabilidade dificulta encontrar a causa raiz.

Tarefas pendentes e futures

Quando os workers são encerrados abruptamente, tarefas em execução não concluem normalmente. Os objetos Future podem terminar com exceções relacionadas à quebra do pool, como BrokenProcessPool. Tarefas ainda não iniciadas também podem ser canceladas ou falhar conforme o estado do executor.

from concurrent.futures.process import BrokenProcessPool

for future in futures:
    try:
        resultado = future.result()
    except BrokenProcessPool:
        registrar_falha("pool encerrado abruptamente")
    except Exception as exc:
        registrar_falha(str(exc))

Não presuma que uma tarefa interrompida pode ser simplesmente repetida. Antes de reenviar, verifique se ela já produziu efeitos parciais. Operações de pagamento, envio de mensagens e alterações em banco exigem chaves de idempotência.

Encerramento com timeout

Uma estratégia robusta combina timeout, tentativa cooperativa e escalonamento. Primeiro, aguarde um prazo razoável. Depois, tente cancelar futures que ainda não começaram. Em seguida, termine os workers. Só use encerramento forçado se a aplicação continuar presa.

from concurrent.futures import wait

concluidas, pendentes = wait(futures, timeout=30)

if pendentes:
    for future in pendentes:
        future.cancel()
    executor.terminate_workers()

Esse desenho evita aplicar uma medida extrema cedo demais. Também separa tarefas lentas de tarefas realmente travadas.

Não reutilize o executor

Após terminate_workers() ou kill_workers(), o executor não pode voltar ao estado saudável. Crie uma nova instância caso precise continuar processando.

executor.terminate_workers()
executor = ProcessPoolExecutor(max_workers=2)

A recriação deve acontecer apenas depois de entender se a causa do travamento continua presente. Caso contrário, a aplicação pode entrar em um ciclo infinito de criar e destruir pools.

Cuidados com arquivos e bancos

Processos interrompidos podem deixar arquivos temporários, locks externos e registros incompletos. Prefira gravar em arquivo temporário e usar renomeação atômica ao final. Em bancos, use transações curtas e confirme somente depois que o resultado estiver completo.

Também evite compartilhar estado mutável complexo entre workers. Filas, arquivos e serviços externos devem ter protocolos claros de retomada. O processo principal precisa ser capaz de identificar itens não concluídos.

Compatibilidade entre versões

Como os métodos são recentes, projetos que suportam versões anteriores do Python devem detectar sua existência. Uma abordagem simples é usar hasattr e manter um fallback.

if hasattr(executor, "terminate_workers"):
    executor.terminate_workers()
else:
    executor.shutdown(wait=False, cancel_futures=True)

O fallback não é idêntico: shutdown(wait=False) não garante a interrupção imediata de processos bloqueados. Por isso, documente a diferença de comportamento.

Supervisão externa

Para sistemas críticos, não confie apenas no processo Python para supervisionar a si mesmo. Systemd, Kubernetes, Docker ou outro supervisor podem aplicar limites de memória, reiniciar o serviço e encerrar todo o grupo de processos se necessário.

O controle interno é útil para recuperar tarefas e registrar contexto. A supervisão externa protege a máquina quando o próprio processo principal também está comprometido.

Observabilidade e diagnóstico

Antes de encerrar o pool, registre o número de tarefas pendentes, duração, tipo de trabalho e identificadores correlacionáveis. Depois, registre qual método foi usado. Métricas de tempo de execução e quantidade de reinicializações ajudam a detectar regressões.

Para investigar travamentos em processos em execução, consulte também pdb -p no Python. A documentação oficial de concurrent.futures descreve o contrato do executor, enquanto a documentação de multiprocessing explica os efeitos de terminate e kill.

Boas práticas finais

  • Use timeouts em operações potencialmente bloqueantes.
  • Projete tarefas idempotentes e reiniciáveis.
  • Prefira terminate_workers() antes de kill_workers().
  • Não reutilize um executor encerrado.
  • Trate BrokenProcessPool e estados parciais.
  • Combine controle interno com supervisão externa.

terminate_workers() e kill_workers() dão ao ProcessPoolExecutor uma saída clara para situações em que o desligamento tradicional não funciona. Eles não substituem um bom desenho de concorrência, mas tornam a recuperação de falhas mais explícita. Usados com timeout, idempotência, logs e supervisão, ajudam a impedir que um único worker travado paralise toda a aplicação.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Terminal de linha de comando usado em uma ferramenta Python com argparse
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    argparse suggest_on_error: melhore erros de CLI

    Aprenda argparse suggest_on_error no Python para sugerir opções corretas, melhorar erros de CLI e manter compatibilidade entre versões.

    Ler mais

    Tempo de leitura: 5 minutos
    18/09/2026
    Código e estrutura de arquivos para compressão Zstandard no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: compacte dados com Zstandard

    Aprenda a compactar e descompactar dados com compression.zstd no Python, usando streams, dicionários e limites seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    18/09/2026
    Programador gerenciando uma fila assíncrona com asyncio.Queue.shutdown
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    asyncio.Queue.shutdown: encerre filas sem deadlocks

    Aprenda asyncio.Queue.shutdown no Python para encerrar filas, liberar workers, drenar tarefas e evitar deadlocks em pipelines assíncronos.

    Ler mais

    Tempo de leitura: 6 minutos
    17/09/2026
    Depuração de processo Python em execução com pdb -p
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p no Python: depure processos

    Aprenda a usar pdb -p no Python para anexar o depurador a processos em execução, analisar travamentos e investigar pilhas

    Ler mais

    Tempo de leitura: 7 minutos
    17/09/2026
    Código Python processado em lotes com itertools.batched
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    itertools.batched strict: valide lotes completos

    Aprenda itertools.batched com strict no Python para criar lotes, validar grupos completos e processar dados com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    16/09/2026
    Análise de dados e cálculos com math.sumprod no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    math.sumprod: produtos escalares e médias ponderadas

    Aprenda math.sumprod no Python para produtos escalares, médias ponderadas, custos e cálculos numéricos claros e eficientes.

    Ler mais

    Tempo de leitura: 5 minutos
    16/09/2026