Módulo 8 - Concorrência com threads

Concorrência, paralelismo e o GIL

10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

Velocidade

O que você vai aprender

  • Distinguir concorrência de paralelismo com clareza.
  • Entender o que é o GIL e por que ele existe.
  • Reconhecer o que o GIL impede e o que ele não impede.
  • Prever quando threads ajudam e quando não fazem diferença.

Duas ideias que parecem a mesma

Muita gente usa concorrência e paralelismo como sinônimos, e não são. Concorrência é uma questão de estrutura: você organiza o programa em tarefas que podem avançar de forma independente, e o sistema alterna entre elas rápido o bastante para parecer que rodam juntas. Paralelismo é uma questão de execução: você tem vários núcleos de processador fazendo trabalho de fato no mesmo instante. Um cozinheiro que cuida de três panelas revezando a atenção é concorrente; três cozinheiros, um em cada panela, são paralelos. Dá para ter concorrência sem paralelismo, e é exatamente isso que as threads do Python entregam na maioria dos casos.

Concorrência

  • Estrutura: várias tarefas em andamento
  • Alterna entre tarefas rapidamente
  • Funciona até com um único núcleo
  • Ideal para tarefas que esperam (I/O)

Paralelismo

  • Execução: várias tarefas no mesmo instante
  • Usa vários núcleos ao mesmo tempo
  • Exige mais de um núcleo de verdade
  • Ideal para cálculo pesado (CPU)

O GIL, a trava que explica tudo

O GIL, ou Global Interpreter Lock, é uma trava global do interpretador CPython, a implementação padrão do Python. A regra dele é simples e drástica: só uma thread pode executar bytecode Python por vez. Mesmo que a sua máquina tenha oito núcleos, duas threads suas nunca rodam código Python puro ao mesmo tempo; elas se revezam, uma segurando a trava enquanto a outra espera. O GIL existe por um bom motivo. Ele simplifica o gerenciamento de memória do interpretador e protege as estruturas internas contra corrupção quando várias threads mexem nos mesmos objetos. O preço desse conforto é o paralelismo de CPU.

A grande sacada é entender quando o GIL é liberado. Quando uma thread faz uma operação de entrada e saída, como ler de um arquivo, chamar uma URL ou consultar um banco, ela solta o GIL enquanto espera a resposta. Nesse intervalo, outra thread pega a trava e trabalha. Como programas de rede passam a maior parte do tempo esperando, e não calculando, o revezamento acontece o tempo todo e o ganho é real. Já uma thread que só faz conta, sem parar para esperar nada, segura o GIL do começo ao fim, e as outras ficam na fila. Por isso threads não aceleram cálculo puro no CPython.

Quando threads valem a pena

A conclusão prática cabe em uma frase: threads no Python servem para tarefas que esperam, não para tarefas que calculam. Se o seu programa baixa cinquenta páginas da internet, cada download passa quase todo o tempo esperando a resposta do servidor. Com threads, enquanto uma espera, as outras já disparam seus pedidos, e o tempo total despenca. Esse é o cenário I/O bound, o reino das threads. Já se o programa precisa fatorar números gigantes ou processar imagens pixel a pixel, ele é CPU bound: o gargalo é a conta, o GIL serializa tudo e as threads só adicionam overhead. Aí o caminho é multiprocessing ou async, que você vê nos próximos módulos.

Tipo de tarefaGargaloThreads ajudam?
Baixar várias URLsEspera de rede (I/O)Sim, muito
Ler e escrever muitos arquivosEspera de disco (I/O)Sim
Consultar um banco várias vezesEspera do banco (I/O)Sim
Fatorar números grandesCálculo puro (CPU)Não, use processos
Processar imagens em massaCálculo puro (CPU)Não, use processos

A regra de ouro: threads brilham em I/O bound e patinam em CPU bound.

Teste rápido

Por que criar dez threads para fatorar dez números enormes não acelera o programa no CPython?

Perguntas frequentes

Se o GIL atrapalha, por que o Python não o remove de vez?
Porque ele simplifica muito o interpretador e o código de extensões em C, além de proteger a memória sem travas espalhadas por toda parte. Removê-lo sem quebrar compatibilidade e sem deixar código de thread única mais lento é um problema difícil, no qual a comunidade trabalha há anos. Há experimentos de um Python sem GIL, mas o padrão ainda o assume.
Concorrência sempre deixa o programa mais rápido?
Não. Ela ajuda quando há tempo ocioso a aproveitar, como espera de rede ou disco. Se a tarefa é puro cálculo, adicionar concorrência com threads pode até piorar, por causa do overhead de trocar de contexto sem ganho de paralelismo. Meça antes de decidir, como o curso sempre reforça.
Qual a diferença entre thread e processo?
Threads vivem dentro do mesmo processo e compartilham a memória, o que as torna leves mas sujeitas ao GIL e a race conditions. Processos são independentes, cada um com sua memória e seu interpretador, então rodam em paralelo de verdade, ao custo de mais memória e de comunicação mais cara entre eles.
O GIL torna as threads inúteis, então?
De jeito nenhum. Para o caso mais comum de concorrência no dia a dia, que é I/O bound, threads são simples e eficazes. Baixar dezenas de páginas, ler vários arquivos ou consultar serviços em paralelo lógico fica muito mais rápido com threads, justamente porque o GIL é liberado durante a espera.
Como eu descubro se meu programa é I/O bound ou CPU bound?
Observe onde o tempo é gasto. Se o programa fica parado esperando respostas de rede, disco ou banco, é I/O bound. Se ele mantém o processador ocupado com contas, é CPU bound. O módulo de performance mostra como medir isso com profiling em vez de adivinhar.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.