githublinkedinemail
← /articles

GIL: o lock que todo Rubyist precisa entender

Ruby não tem paralelismo real por causa do GIL — meia-verdade dita com confiança. Entenda o GVL de verdade, quando threads ajudam e quando viram armadilha.

3 minruby

GIL: o lock que todo Rubyist precisa entender

Existe uma frase repetida em conversa de dev como verdade absoluta:

"Ruby não tem paralelismo real por causa do GIL."

Meia-verdade.

E meia-verdade dita com confiança é pior que mentira inteira.


Primeiro: GIL ou GVL?

No MRI moderno, o nome correto é GVL (Global VM Lock).

GIL é um termo herdado do Python.

Funciona igual na prática: só uma thread executa código Ruby por vez dentro de um processo.

Mas o "código Ruby" aí é a parte importante.

Porque IO não conta.


O que o GVL realmente faz

O GVL serializa execução de bytecode YARV.

Quando uma thread está rodando código Ruby, ela segura o lock.

Quando ela vai esperar:

  • leitura de arquivo
  • conexão HTTP
  • query no Postgres
  • sleep

…ela libera o GVL.

Outra thread assume e roda código Ruby naquele intervalo.

Por isso threads em Ruby ainda valem a pena. Você só precisa entender pra quê.


Quando threads ajudam

threads = urls.map do |url|
  Thread.new { Net::HTTP.get(URI(url)) }
end
threads.each(&:join)

100 requisições HTTP em paralelo?

Funciona.

Cada thread fica bloqueada esperando rede. O GVL é liberado. Outra thread assume.

Throughput sobe MUITO.


Quando threads NÃO ajudam

threads = (1..4).map do
  Thread.new do
    1_000_000.times { Math.sqrt(rand) }
  end
end
threads.each(&:join)

Quatro threads rodando cálculo puro?

Não acelera nada.

Porque é CPU-bound. Todas competem pelo mesmo GVL.

Você adiciona overhead de troca de contexto e ganha zero de paralelismo.

Pior: pode ficar mais lento.


A imagem mental certa

Processo Ruby
├── GVL [🔒]
├── Thread 1 — segura o lock → roda código
├── Thread 2 — esperando lock
├── Thread 3 — esperando lock
└── Thread 4 — fazendo IO (liberou o lock)

GVL = um único microfone num grupo de 4 pessoas falando.

Só quem está com o microfone fala.

Quando alguém vai beber água (IO), passa o microfone.


Como conviver com isso

Existem três caminhos:

1. Múltiplos processos

Puma worker 1 — GVL próprio
Puma worker 2 — GVL próprio
Puma worker 3 — GVL próprio

Web app Ruby moderna usa esse modelo. Cada worker tem seu GVL. Paralelismo real entre eles.

Custo: mais memória.

2. Threads para IO-bound

Sidekiq processa jobs em threads.

Funciona porque jobs geralmente fazem queries, chamam APIs, escrevem em disco.

IO o tempo todo.

3. Saia do MRI

  • JRuby: roda na JVM, sem GVL. Threads de verdade.
  • TruffleRuby: roda no GraalVM, sem GVL.
  • Ractor: experimento dentro do próprio MRI, contornando o GVL com isolamento.

Cada um tem trade-off. Nenhum é grátis.


Por que o GVL ainda existe?

Porque o MRI tem dependências em C que assumem single-thread.

Tirar o GVL quebraria gems do ecossistema inteiro.

A galera da equipe principal do Ruby vem trabalhando em alternativas (Ractor, fibers). Mas a transição é gradual.

E precisa ser. Migração quebrada destrói confiança no ecossistema.


O erro de raciocínio mais comum

"Vou criar 50 threads pra processar essa lista mais rápido."

Se for cálculo: você acabou de criar overhead sem ganho.

Se for IO: pode funcionar — mas atenção a pool de conexão, memory pressure, GC pressure.

Threads são poderosas. Threads também são armadilha.

A diferença é entender o que está bloqueando.


A grande virada de chave

GVL não te impede de fazer concorrência.

GVL te impede de fazer paralelismo de CPU dentro de um processo.

São coisas diferentes.

Concorrência = lidar com várias coisas que esperam.
Paralelismo = executar várias coisas ao mesmo tempo.

Web app é concorrência.
Processamento numérico pesado é paralelismo.

Use a ferramenta certa pra cada um.


Conclusão

GVL não é defeito.

É trade-off.

Ele simplifica o runtime, mantém compatibilidade com extensões em C, e ainda permite concorrência via IO.

Se você está sofrendo com ele, pode ser que esteja usando Ruby pra algo que ele não foi feito.

Ou que está usando threads do jeito errado.

Quem entende o GVL para de brigar com ele.

E começa a desenhar sistemas em volta dele.


GIL: o lock que todo Rubyist precisa entender
GIL: o lock que todo Rubyist precisa entender