Concorrência em Ruby

"Ruby não escala!"

Quem nunca ouviu essa frase antes? Ela tem um fundo de verdade, mas muita inconsistência. Neste módulo, vamos abordar todos os aspectos de concorrência em Ruby e tentar desmistificar algumas afirmações como "não dá pra paralelizar com Ruby" ou até mesmo "Ruby não tem multi-threading".

Estas afirmações costumam vir carregadas de desconhecimento. Ruby traz um suporte muito robusto a concorrência, inclusive nas versões modernas.

Muito do que dizem "Ruby é lento" é na verdade com relação ao Ruby antigo 1.8, que vamos comentar um pouco mais a seguir

Já viu o módulo "Concorrência em C" antes?

Se você chegou até aqui, acredito que já tenha lido o primeiro módulo deste capítulo II sobre Concorrência em C. Caso contrário, apesar de que seja altamente recomendado que faça primeiro o módulo de C (não é complexo, eu garanto), cada módulo sobre as linguagens de alto nível foi pensado para ser independente.

Portanto, se você já tem um bom conhecimento sobre concorrência no geral e todo o vocabulário envolvido mas tem interesse em apenas aprender como funciona concorrência em Ruby, não há problema se quiser ler apenas este módulo.

Entretanto se você pular o módulo de C, pode ser que se sinta perdido(a) em alguns pontos, mas recomendo fortemente que leia antes a primeira parte do guia, sobre Concorrência no Sistema Operacional


Preâmbulo

A implementação de Ruby mais amplamente utilizada é o interpretador CRuby (escrita em C), também conhecido popularmente como MRI, que vem de Matz Ruby Interpreter.

Se você tem interesse em algo introdutório sobre a linguagem Ruby, eu escrevi este artigo que aborda sua história, principais características e funcionalidades.

Os exemplos aqui trazidos funcionam na última versão estável do interpretador atualmente - Jan/2025 -, que é a 3.4.1 .

No que diz respeito à concorrência, Ruby 3.4.1 traz consigo na biblioteca padrão:

  • Forking de processos

  • Kernel Threads e sincronização de threads

  • Modelo de atores (ainda experimental)

  • Fibers (porém o escalonamento precisa ser implementado por nós)

  • I/O não-bloqueante e monitoramento de descritores, através de IO.select

E através de bibliotecas externas (gems), podemos ter tudo o que a biblioteca padrão já traz, inclusive:

  • Implementações mais robustas de threading/forking

  • Implementação de Thread Pool (embora não seja complexo implementar uma pool, como vimos em C, pelo que em Ruby é flocos com morango)

  • I/O assíncrono, com loop de eventos, suporte a epoll etc

Vamos ver tudo isto em detalhes nos próximos tópicos.

Last updated