multiprocessing no Python: vários núcleos

Publicado em: 26/08/2026
Tempo de leitura: 7 minutos
Close-up view of a computer screen displaying code in a software development environment.

O módulo multiprocessing permite executar código Python em processos separados. Cada processo possui seu próprio interpretador e espaço de memória, o que possibilita usar vários núcleos para tarefas limitadas por CPU. O módulo também oferece filas, pipes, locks, memória compartilhada, pools, managers e primitivas de sincronização com uma API inspirada em threading.

Processos não são threads mais rápidas. Eles custam mais para iniciar, consomem mais memória e exigem serialização para transferir objetos. Recursos como conexões, sockets, locks e arquivos precisam de regras claras de propriedade. Um desenho correto começa pela escolha do método de início e pela redução da comunicação entre workers.

Proteja o ponto de entrada

Em plataformas que iniciam processos importando o módulo principal, o código de criação precisa estar protegido.

from multiprocessing import Process


def trabalhar(valor):
    print(valor * 2)

if __name__ == "__main__":
    processo = Process(target=trabalhar, args=(21,))
    processo.start()
    processo.join()

Sem a proteção, cada filho pode tentar criar novos filhos durante o import, causando recursão, erros ou uma tempestade de processos.

Process, start e join

Process representa um processo filho. start() solicita a criação; join() espera a conclusão; exitcode informa como terminou.

processo.start()
processo.join(timeout=10)
if processo.is_alive():
    processo.terminate()
    processo.join()
print(processo.exitcode)

Um timeout de join() não mata o processo. Depois do limite, o coordenador deve decidir entre esperar, cancelar cooperativamente ou terminar à força.

Métodos de início

Os métodos mais conhecidos são spawn, fork e forkserver, com disponibilidade e padrões que variam por plataforma. spawn inicia um interpretador novo; fork copia o processo atual; forkserver usa um servidor dedicado para criar filhos.

Escolha explicitamente quando o comportamento fizer parte da arquitetura.

import multiprocessing as mp

if __name__ == "__main__":
    contexto = mp.get_context("spawn")
    processo = contexto.Process(target=trabalhar, args=(10,))
    processo.start()
    processo.join()

Cuidados com fork

fork herda memória, descritores e estado de bibliotecas. Em programas com threads, locks mantidos por threads que desaparecem podem permanecer bloqueados no filho. Clientes de banco, TLS, logging e runtimes nativos também podem ficar inconsistentes.

Consulte posix no Python para entender a camada Unix. Em aplicações complexas, spawn costuma ser mais previsível, embora tenha startup maior.

Serialização com pickle

Argumentos enviados a processos, itens de filas e resultados de pools geralmente são serializados. Funções devem estar no nível do módulo; lambdas, closures e recursos ativos podem não ser compatíveis.

Não desserialize dados de origem não confiável. O mecanismo de serialização do Python pode executar código durante a reconstrução.

Queue

Queue oferece comunicação segura entre processos com uma interface semelhante à fila de threads.

from multiprocessing import Process, Queue


def produtor(fila):
    for numero in range(5):
        fila.put(numero * numero)
    fila.put(None)

if __name__ == "__main__":
    fila = Queue(maxsize=10)
    processo = Process(target=produtor, args=(fila,))
    processo.start()
    while True:
        item = fila.get()
        if item is None:
            break
        print(item)
    processo.join()

Uma fila limitada aplica backpressure. Defina um protocolo de encerramento, como sentinela, evento ou fechamento explícito.

Evite deadlock ao juntar processos

Um processo que escreveu muitos dados em uma fila pode esperar que o buffer seja consumido antes de terminar. Se o pai chama join() antes de ler a fila, ambos podem ficar bloqueados.

Consuma mensagens enquanto o filho trabalha ou use a ordem recomendada para fechar e juntar filas.

Pipe

Pipe() cria duas conexões. É adequado para comunicação ponto a ponto.

from multiprocessing import Pipe, Process


def filho(conexao):
    conexao.send({"status": "ok"})
    conexao.close()

if __name__ == "__main__":
    pai, ponta_filho = Pipe(duplex=False)
    processo = Process(target=filho, args=(ponta_filho,))
    processo.start()
    ponta_filho.close()
    print(pai.recv())
    processo.join()

Feche pontas não usadas. Descritores extras mantidos abertos podem impedir detecção de EOF.

Pool

Pool mantém um conjunto de workers para várias tarefas.

from multiprocessing import Pool


def cubo(numero):
    return numero ** 3

if __name__ == "__main__":
    with Pool(processes=4) as pool:
        print(pool.map(cubo, range(10)))

Para uma API uniforme entre threads e processos, veja concurrent.futures no Python.

map, imap e unordered

map() coleta resultados em ordem e pode manter muitos dados. imap() entrega gradualmente; imap_unordered() entrega conforme a conclusão.

Use chunksize para agrupar entradas. Lotes pequenos aumentam overhead; lotes grandes reduzem balanceamento e atrasam resultados.

apply_async e callbacks

apply_async() retorna um resultado assíncrono e pode receber callbacks.

with Pool(4) as pool:
    resultado = pool.apply_async(cubo, (5,))
    print(resultado.get(timeout=5))

Callbacks executam no processo coordenador e devem ser curtos. Erros precisam de error_callback ou coleta explícita.

Feche o pool corretamente

close() impede novas tarefas, join() espera workers e terminate() encerra abruptamente. O context manager facilita cleanup, mas compreenda a política usada no caso de exceção.

Não deixe pools globais serem finalizados apenas pelo coletor de lixo.

Events e cancelamento cooperativo

Um Event compartilhado permite pedir parada.

from multiprocessing import Event


def worker(parar):
    while not parar.is_set():
        executar_bloco()

O worker deve verificar o evento em intervalos razoáveis e usar timeouts em operações bloqueantes.

terminate e kill

Encerramento forçado pode deixar locks adquiridos, filas corrompidas, arquivos incompletos e recursos externos abertos. Use somente depois de uma tentativa cooperativa e com um plano de recuperação.

Não termine um processo enquanto ele atualiza dados críticos sem protocolo transacional.

Locks e semáforos

Lock, RLock, Semaphore, BoundedSemaphore, Condition e Barrier coordenam processos. Use a primitiva mais simples e mantenha seções críticas curtas.

from multiprocessing import Lock, Value

lock = Lock()
contador = Value("i", 0)

with lock:
    contador.value += 1

Uma exceção dentro da seção deve liberar o lock; por isso, prefira context manager.

Value e Array

Value e Array armazenam tipos C em memória compartilhada. O acesso pode ser sincronizado.

São úteis para pequenos estados, mas estruturas complexas ficam difíceis de manter. Muitas vezes uma fila com mensagens imutáveis é mais segura.

shared_memory

O módulo de memória compartilhada permite compartilhar blocos de bytes sem copiar grandes arrays entre processos. Todos os participantes precisam concordar com layout, tamanho, tipo e ciclo de vida.

O criador deve fechar e desvincular o bloco no momento correto. Um crash pode deixar recursos órfãos.

Managers

Manager cria um processo servidor que oferece proxies para listas, dicionários, locks e outros objetos. É conveniente, mas cada operação envolve comunicação e serialização.

Não use um dicionário gerenciado em loops de alta frequência. Agrupe alterações ou redesenhe o fluxo com filas.

Compartilhe menos

A arquitetura mais robusta costuma enviar uma tarefa autocontida ao worker e receber um resultado autocontido. Estado compartilhado aumenta acoplamento, sincronização e risco de deadlock.

Particione os dados para que cada processo possua sua região.

Inicialização de worker

Pools aceitam uma função de inicialização para abrir recursos por processo. Conexões criadas no processo pai não devem ser reutilizadas cegamente pelos filhos.

conexao = None

def iniciar_worker():
    global conexao
    conexao = abrir_conexao()

Feche recursos na saída e evite armazenar segredos em logs.

Memória e copy-on-write

Em sistemas com fork, páginas podem ser compartilhadas até serem modificadas. Isso pode reduzir startup, mas qualquer escrita aumenta memória. Objetos de Python e garbage collection podem tocar páginas inesperadamente.

Meça resident set de todos os processos. O artigo sobre resource no Python ajuda a observar limites Unix.

Tratamento de exceções

Uma exceção em um worker deve ser coletada pelo pai. Em pools, chamar get() ou iterar resultados propaga a falha. Se o programa nunca coleta o resultado, o erro pode passar despercebido.

Inclua identificador da tarefa e preserve traceback.

Logging

Vários processos escrevendo no mesmo arquivo podem misturar registros. Use um QueueHandler e um processo ou thread central para gravar logs.

Inclua PID, nome do processo, tarefa e tentativa.

Signals e shutdown

Defina o coordenador como responsável por sinais e distribua cancelamento aos workers. Signal handlers devem ser mínimos. Consulte signal no Python.

Durante shutdown, pare a produção, envie sentinelas, espere um prazo, finalize workers restantes e feche filas.

Executáveis congelados

Aplicações empacotadas podem precisar de freeze_support(). Teste o executável real, não apenas o script no ambiente de desenvolvimento.

Segurança

Não exponha managers ou conexões de multiprocessing em rede sem autenticação e isolamento. Não aceite payloads serializados de clientes não confiáveis.

Limite processos, memória e tarefas para evitar exaustão local.

Testes

Teste diferentes métodos de início, poucos e muitos workers, exceções, crash, fila cheia, shutdown, Ctrl+C, worker travado, dados grandes, ambiente sem fork e executável empacotado.

Evite depender de sleeps exatos. Use eventos e timeouts amplos.

Erros comuns

Os erros mais frequentes são esquecer a proteção do módulo principal, enviar lambdas, compartilhar conexões herdadas, chamar join() antes de consumir filas, usar Manager para tudo, terminar processos durante escrita, não coletar exceções, criar workers demais e deixar memória compartilhada órfã.

Conclusão

multiprocessing permite paralelismo real e comunicação entre processos, mas exige disciplina com serialização, lifecycle e recursos. Prefira tarefas independentes, comunicação limitada, cancelamento cooperativo e pools com tamanho controlado.

Consulte a documentação oficial de multiprocessing e compare com concurrent.futures para uma interface mais compacta.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Monochrome image showcasing concentric circles in a tunnel-like structure creating a sense of depth.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    concurrent.futures: threads e processos em paralelo

    Aprenda concurrent.futures no Python com threads, processos, Future, timeouts, cancelamento, backpressure e prevenção de deadlocks.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Striking image of a red-bellied python showcasing its vibrant scales in dramatic lighting.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    winsound no Python: sons no Windows

    Aprenda winsound no Python para tocar WAV, sons do sistema, beeps, loops e notificações assíncronas com segurança no Windows.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    winreg no Python: Registro do Windows

    Aprenda winreg no Python para ler e gravar o Registro do Windows, controlar permissões, tipos, WOW64, exclusões e segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    Macro shot capturing detailed patterns of a python in its natural surroundings.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    posix no Python: chamadas Unix diretas

    Entenda posix no Python, chamadas Unix, descritores, permissões, processos, segurança e quando usar os em vez do módulo direto.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    A male software engineer working on code in a modern office setting.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    curses no Python: interfaces no terminal

    Aprenda curses no Python para criar interfaces de terminal com janelas, cores, teclado, resize, Unicode e cleanup seguro.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Detailed close-up of a reticulated python showcasing intricate scales and piercing eyes.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    grp no Python: consulte grupos Unix

    Aprenda grp no Python para consultar grupos Unix, GIDs, membros, ownership e grupos suplementares com NSS e containers.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026