Desde a versão 1.0, o CPython usa o GIL (Global Interpreter Lock): um mutex único que garante que apenas uma thread execute bytecode Python por vez, mesmo em máquinas com vários núcleos. Isso simplifica o interpretador e protege estruturas internas de condições de corrida, mas também significa que código Python multithread nunca conseguiu paralelismo real de CPU — só ajuda quando as threads passam a maior parte do tempo esperando I/O. O Python free-threaded (PEP 703) é uma build alternativa do CPython que remove o GIL, permitindo que múltiplas threads executem bytecode Python simultaneamente em núcleos diferentes.
O problema que o GIL sempre causou
Um programa que usa threading para dividir trabalho pesado de CPU entre threads não ganha velocidade proporcional aos núcleos disponíveis, porque o GIL serializa a execução:
import threading
import time
def contar(n):
while n > 0:
n -= 1
inicio = time.perf_counter()
threads = [threading.Thread(target=contar, args=(30_000_000,)) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"{time.perf_counter() - inicio:.2f}s")
Na build padrão do CPython, esse código com 4 threads não fica muito mais rápido do que rodar as quatro contagens em sequência — o GIL alterna entre as threads, mas só uma roda bytecode por vez. Por isso, o padrão histórico para paralelismo real em Python sempre foi multiprocessing, que contorna o GIL criando processos separados, com o custo de serialização entre eles (pickle) e maior uso de memória.
Como funciona a build free-threaded
A partir do Python 3.13, o instalador oficial passou a oferecer uma build experimental sem o GIL, identificada pelo sufixo t (de threading). Para usá-la é preciso instalar esse binário específico — o Python padrão continua com o GIL ativado:
python3.13t --version
# Python 3.13.x (com suporte a free-threading habilitado)
Internamente, a build free-threaded substitui o lock único por mecanismos mais finos: contagem de referências thread-safe (biased reference counting) e locks específicos por objeto, em vez de um lock global para o interpretador inteiro. O resultado é que o mesmo código de contagem acima, rodado no python3.13t, de fato distribui o trabalho entre os núcleos disponíveis e reduz o tempo total proporcionalmente ao número de threads.
Verificando em runtime se o GIL está desabilitado
O módulo sys ganhou uma forma de checar isso programaticamente, útil para logs de diagnóstico ou testes condicionais:
import sys
if hasattr(sys, "_is_gil_enabled"):
print("GIL ativo:", sys._is_gil_enabled())
else:
print("Build sem suporte a free-threading")
Uma extensão C ainda não adaptada para o modo free-threaded pode reativar o GIL automaticamente ao ser importada, como medida de segurança — nesse caso o programa continua funcionando, só perde o ganho de paralelismo enquanto aquela extensão estiver carregada.
Exemplo prático: processamento paralelo de CPU
Dividir uma lista grande de trabalho entre threads, sem recorrer a processos separados, passa a fazer sentido para tarefas de CPU:
from concurrent.futures import ThreadPoolExecutor
def eh_primo(n):
if n < 2:
return False
return all(n % d for d in range(2, int(n**0.5) + 1))
numeros = range(2, 200_000)
with ThreadPoolExecutor(max_workers=8) as executor:
primos = list(executor.map(eh_primo, numeros))
print(sum(primos), "números primos encontrados")
No CPython padrão, esse ThreadPoolExecutor não traria ganho de desempenho relevante para uma tarefa desse tipo, dominada por CPU. Numa build free-threaded, com eh_primo sem dependências externas, o trabalho é de fato paralelizado entre as 8 threads.
Quando usar
- Cargas de trabalho de CPU que hoje usam
multiprocessingsó para contornar o GIL, e sofrem com o custo de serializar dados entre processos. - Bibliotecas científicas e de dados (NumPy, entre outras já compatíveis) que processam arrays grandes e podem dividir o trabalho entre threads sem cópias extras de memória.
- Servidores que combinam I/O assíncrono com etapas pesadas de CPU, aproveitando threads para ambos os casos sem alternar para processos.
Cuidados
Remover o GIL expõe código que sempre dependeu dele implicitamente para evitar condições de corrida — estruturas de dados compartilhadas sem lock explícito, que "funcionavam por acaso" porque o GIL serializava tudo, podem se comportar de forma diferente na build free-threaded. Extensões em C que usam a API C do CPython diretamente também precisam ser adaptadas (a maioria das bibliotecas populares ainda está em processo de suporte). Até o Python 3.13, a build free-threaded era experimental e não era a distribuída por padrão; é preciso instalá-la explicitamente.
Compatibilidade
A PEP 703 introduziu a build free-threaded como experimental no Python 3.13 (outubro de 2024). A PEP 779 promoveu esse suporte a oficialmente suportado a partir do Python 3.14 (outubro de 2025) — deixou de ser experimental, mas ainda não é a build padrão instalada. O overhead em código single-thread, que era de cerca de 40% nas primeiras versões, caiu para a faixa de 5-10% no Python 3.14.
Conclusão
O free-threading não torna o Python multithread mais rápido em todos os casos — código dominado por I/O já se beneficiava do GIL sendo liberado durante esperas — mas resolve o problema histórico de paralelismo real de CPU, que antes só o multiprocessing endereçava. Ainda é uma mudança em transição, com bibliotecas se adaptando, mas já dá para experimentar com o binário python3.13t ou python3.14t hoje.
0 Comentário
Deixe seu Comentário!
Dúvida, sugestão ou só um "valeu!" — tudo é bem-vindo por aqui.