Três modelos, uma decisão
Um ticket cai no seu quadro: "a página de checkout chama quatro serviços e leva 1,8 segundo". Você já sabe que a correção é parar de chamá-los um depois do outro. O que você precisa decidir é qual modelo de concorrência do Ruby usar, e essa decisão tem três candidatos reais, e não um.
O Ruby oferece threads, processos e Ractors. Eles não são três versões da mesma coisa. Eles diferem em dois eixos que decidem todo o resto. Eles rodam código Ruby ao mesmo tempo em núcleos diferentes? E eles enxergam os objetos uns dos outros?
| Modelo | Paralelismo real | Compartilha objetos | Custo para iniciar |
|---|---|---|---|
| Thread | Não, para código Ruby | Sim, todos | Barato |
| Processo | Sim | Não | Caro |
| Ractor | Sim | Só os compartilháveis | Moderado |
| Fiber | Não | Sim, na mesma thread | Muito barato |
Essa tabela é o curso. O resto é detalhe sobre como cada linha se comporta, e em qual linha a sua carga de trabalho se encaixa.
O fato que você precisa saber logo
No CRuby, threads não rodam código Ruby em paralelo. Uma trava global deixa só uma thread interpretar Ruby por vez. Então quatro threads fazendo só cálculo terminam em mais ou menos o mesmo tempo de relógio que uma thread fazendo as quatro partes em sequência.
Aqui está isso medido na máquina em que este curso foi escrito, com Ruby 4.0.1, somando quadrados até vinte milhões, duas vezes:
sequencial 2.01s
threads 2.01s
ractors 1.32s
As threads não ganharam nada, e os Ractors cortaram o tempo em cerca de um terço. Uma nova medição em outra máquina, em 2026-09-28, deu 1,64 s em sequência, 1,67 s com threads e 0,82 s com Ractors. O formato se manteve, e lá os Ractors cortaram o tempo pela metade. É o formato inteiro da decisão numa medição só.
O que a trava protege, quando ela é liberada e como medir o efeito dela direito é assunto grande o bastante para um curso próprio: o curso de nível Sênior "Ruby: mergulho no GVL", que vem logo depois deste. Aqui você precisa da consequência, e não do mecanismo. Threads servem para esperar, e não para calcular.
Esperar não é calcular
Essa distinção faz mais trabalho do que parece. Quase tudo o que um processo Rails faz é esperar: o Postgres responder, uma chamada HTTP voltar, um arquivo ser lido. Enquanto uma thread espera I/O, ela não está interpretando Ruby, então outras threads rodam. Dez threads, cada uma esperando 200 ms por um serviço diferente, terminam juntas em cerca de 200 ms, num núcleo, num processo.
Troque essa chamada HTTP pelo parse de um JSON de 40 MB, e as mesmas dez threads levam cerca de dez vezes o tempo de uma. É o mesmo formato de código com o resultado oposto, porque uma carga espera e a outra calcula.
Então a primeira pergunta sobre qualquer carga nunca é "como deixo isso concorrente?". É "isto está esperando ou trabalhando?".
Onde você já está
O curso de nível Júnior "Ruby: fundamentos de Threads" deu a mecânica. Ele cobriu Thread#status, abort_on_exception, um deadlock real com dois mutexes, Queue e SizedQueue, e um benchmark mostrando que threads presas em I/O se sobrepõem, enquanto threads presas em CPU não. Este curso supõe tudo isso e não reensina como iniciar uma thread.
O que muda neste nível é que você deixa de receber o modelo pronto e passa a escolhê-lo, para uma carga que tem uma conta e um plantão atrelados.
O que este curso cobre
A lição 2 trata de threads e Fibers juntas, porque são os dois modelos que compartilham memória dentro de uma mesma linha de controle. Ela cobre em que threads são boas, o que é uma Fiber e onde fica um Fiber scheduler no Ruby 4.0.1.
A lição 3 é sobre processos: fork, copy-on-write e isolamento. É o modelo que os workers do Puma e o Solid Queue usam em produção, com paralelismo real pago em memória.
A lição 4 é sobre Ractors e as regras de compartilhamento. Só objetos compartilháveis atravessam a fronteira; o resto é copiado. Essa regra não é uma limitação colada ao design. Ela é o design.
A lição 5 olha os Ractors com honestidade. Ela cobre o aviso de recurso experimental que a API ainda imprime, e um Ractor::IsolationError real levantado pelo código desta aplicação quando uma classe de model é tocada de um Ractor que não é o principal.
A lição 6 trata a segurança entre threads como algo que você projeta, e não algo que você espera que aconteça. Ela cobre estado mutável compartilhado, Mutex, Queue como primitiva de passagem e estado por requisição, e traz o desafio de código.
A lição 7 aplica um critério de decisão a três cargas reais. Depois ela entra num processo Rails para ver quais modelos já estão rodando lá.
Comece pelas threads. É o modelo que você mais vai usar, e aquele cujos limites são mais fáceis de interpretar mal.
Continue com a aula 2
Threads e Fibers: concorrência sem paralelismo
Mostra como threads sobrepõem esperas de I/O, por que compartilham tudo e onde entram Fibers e fiber schedulers.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.