O parâmetro track de multiprocessing.shared_memory.SharedMemory resolve um problema importante em aplicações Python que compartilham memória entre processos independentes. Ele define se o segmento deve ser registrado no resource tracker do multiprocessing, componente responsável por limpar recursos que poderiam permanecer no sistema após o fim dos processos. Parece um detalhe pequeno, mas uma escolha errada pode remover a memória cedo demais, gerar avisos de vazamento ou deixar segmentos órfãos.
Neste guia, você vai entender quando usar track=True, quando usar track=False, como combinar close() e unlink(), quais diferenças existem entre processos criados pela mesma família e processos iniciados separadamente, além de padrões seguros para Linux, macOS e Windows.
O que é memória compartilhada
Memória compartilhada permite que dois ou mais processos acessem a mesma região de bytes sem serializar grandes objetos a cada troca. Isso é útil em processamento numérico, visão computacional, pipelines de dados e serviços que trabalham com buffers extensos. Em vez de copiar uma matriz de centenas de megabytes por uma fila, um processo cria o segmento e outros processos abrem o mesmo nome.
from multiprocessing import shared_memory
shm = shared_memory.SharedMemory(create=True, size=1024)
try:
shm.buf[:4] = b"PYTH"
finally:
shm.close()
shm.unlink()O método close() fecha o descritor local. O método unlink() solicita a remoção do nome do segmento. Em sistemas POSIX, o conteúdo pode continuar disponível para processos que já o abriram até que o último descritor seja fechado. No Windows, a remoção ocorre quando todos os identificadores são fechados.
Para que serve o parâmetro track
Com track=True, o segmento é registrado no rastreador de recursos do multiprocessing. Se o processo responsável terminar sem executar a limpeza, o rastreador tenta remover o recurso. Esse comportamento é conveniente quando todos os processos pertencem à mesma árvore criada por multiprocessing, porque eles normalmente compartilham uma infraestrutura coordenada.
Com track=False, o objeto não é registrado nesse rastreador. A aplicação passa a assumir integralmente a responsabilidade por definir quem chama unlink(). Isso é especialmente relevante quando processos são iniciados por shells, supervisores, containers ou serviços diferentes.
O problema dos processos independentes
Imagine um produtor iniciado pelo systemd e um consumidor executado manualmente. Ambos abrem o mesmo segmento. Se cada processo possuir seu próprio resource tracker e usar track=True, o primeiro processo que terminar pode fazer o rastreador remover o segmento, embora o outro ainda precise que novos consumidores consigam abri-lo. O processo que já mantém um descritor pode continuar trabalhando em alguns sistemas, mas novas aberturas falharão.
Nesse cenário, costuma ser melhor escolher um proprietário explícito. O proprietário cria o segmento, publica seu nome e executa unlink() apenas no encerramento definitivo. Clientes independentes abrem o segmento com track=False e chamam somente close().
from multiprocessing import shared_memory
# Cliente independente
shm = shared_memory.SharedMemory(
name="dados_academify",
create=False,
track=False,
)
try:
dados = bytes(shm.buf[:128])
finally:
shm.close()Padrão com proprietário único
Um desenho robusto define claramente o ciclo de vida. O processo proprietário cria o segmento, grava uma versão de protocolo no cabeçalho, mantém um bloqueio ou mecanismo de coordenação e remove o recurso apenas depois que os clientes encerram.
from multiprocessing import shared_memory
shm = shared_memory.SharedMemory(
name="cache_python",
create=True,
size=4096,
track=True,
)
try:
shm.buf[:8] = b"CACHE001"
input("Pressione Enter para encerrar")
finally:
shm.close()
shm.unlink()O proprietário pode usar track=True como proteção adicional, desde que os clientes independentes usem track=False. Assim, apenas o ciclo de vida do proprietário participa da limpeza automática.
Processos da mesma aplicação
Quando os workers são criados por multiprocessing.Process, Pool ou outro mecanismo coordenado pela mesma aplicação, track=True geralmente é a opção natural. Ainda assim, não confie somente no encerramento automático. Use blocos try/finally, registre quem é o dono e teste interrupções.
Também é útil comparar com outros recursos de concorrência em artigos como multiprocessing no Python, queue no Python, mmap no Python e contextvars no Python. Cada ferramenta tem um modelo diferente de propriedade, sincronização e limpeza.
close não é unlink
Essa distinção merece atenção. close() libera apenas o acesso daquele objeto local. Ele não remove necessariamente o segmento do sistema. unlink() remove o nome e impede novas conexões, mas não deve ser chamado indiscriminadamente por todos os participantes.
Uma regra prática é: todos os processos chamam close(); somente o proprietário chama unlink(). Quando há failover, documente como um novo proprietário é eleito e como detectar segmentos antigos com segurança.
Compatibilidade no Windows
No Windows, o parâmetro de rastreamento tem efeito diferente porque o próprio sistema remove a memória quando todos os identificadores são fechados. Mesmo assim, escrever código portátil exige cuidado. Não presuma que o comportamento de remoção será idêntico em Linux. Teste o ciclo de vida em cada plataforma suportada e evite colocar unlink() em clientes genéricos.
Validação e formato do buffer
Memória compartilhada não transporta metadados de alto nível automaticamente. Defina um cabeçalho com versão, tamanho, estado e checksum. Antes de ler, confirme que o produtor terminou a gravação. Para estruturas numéricas, combine o buffer com memoryview, struct ou NumPy, mas valide dimensões e tipo.
import struct
versao, tamanho = struct.unpack_from("!II", shm.buf, 0)
if versao != 1:
raise RuntimeError("Versão incompatível")
if tamanho > len(shm.buf) - 8:
raise ValueError("Tamanho inválido")Sincronização
O parâmetro track não oferece bloqueio. Ele cuida apenas do rastreamento do recurso. Para evitar leituras durante uma gravação, use locks, semáforos, eventos, filas de controle ou protocolos de versão. Um padrão simples grava os dados, calcula um checksum e só então altera um campo de estado de “gravando” para “pronto”.
Tratamento de falhas
Considere encerramento abrupto, reinício do produtor e clientes atrasados. Ao iniciar, o proprietário pode verificar se o nome já existe. Não remova automaticamente um segmento desconhecido: ele pode pertencer a outra instância ativa. Use nomes com namespace, identificador da aplicação e ambiente.
Em ambientes com containers, observe também o namespace de IPC. Dois containers podem não enxergar o mesmo segmento, dependendo da configuração. Para detalhes oficiais, consulte a documentação de multiprocessing.shared_memory e a documentação do multiprocessing.
Testes recomendados
Crie testes que iniciem produtor e consumidor, encerrem cada um em ordens diferentes e tentem abrir o segmento novamente. Teste também exceções antes de unlink(), finalização por sinal e reinício. Em Linux, ferramentas como ls /dev/shm ajudam a identificar recursos residuais durante desenvolvimento, mas não devem substituir uma estratégia de ciclo de vida.
Erros comuns
Os erros mais frequentes são permitir que todo cliente execute unlink(), usar track=True em processos independentes sem entender os trackers separados, esquecer close(), armazenar dados sem cabeçalho e tratar memória compartilhada como se fosse thread-safe. Outro erro é reutilizar um nome fixo sem verificar versão e proprietário.
Checklist de produção
Defina um único proprietário, escolha track conforme a topologia dos processos, feche todos os descritores, remova o segmento apenas no fim correto, valide tamanho e versão, sincronize escritores e leitores, monitore recursos órfãos e teste nas plataformas reais.
Conclusão
O parâmetro track não muda como os bytes são compartilhados; ele muda quem participa da limpeza automática. Use track=True quando a família de processos é coordenada pelo multiprocessing e compartilha o mesmo modelo de ciclo de vida. Use track=False em clientes independentes quando existe um proprietário externo responsável pela remoção. Com propriedade explícita, close(), unlink(), sincronização e validação, a memória compartilhada se torna rápida e previsível.







