<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Michael Alves</title>
    <description>The latest articles on Forem by Michael Alves (@michael_d_rei).</description>
    <link>https://forem.com/michael_d_rei</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F888291%2Fb71e45ad-4472-48c8-9c04-b5fc1e84595f.jpg</url>
      <title>Forem: Michael Alves</title>
      <link>https://forem.com/michael_d_rei</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/michael_d_rei"/>
    <language>en</language>
    <item>
      <title>Concorrência e Paralelismo em Ruby</title>
      <dc:creator>Michael Alves</dc:creator>
      <pubDate>Wed, 21 May 2025 22:29:49 +0000</pubDate>
      <link>https://forem.com/michael_d_rei/concorrencia-e-paralelismo-em-ruby-2e2j</link>
      <guid>https://forem.com/michael_d_rei/concorrencia-e-paralelismo-em-ruby-2e2j</guid>
      <description>&lt;p&gt;Software concorrente, paralelismo, múltiplas threads, esses assuntos sempre acabam surgindo, independentemente do seu nível de senioridade. Por muito tempo, esses temas foram verdadeiros pesadelos para mim. Eu me lembro de estudar concorrência usando C na faculdade. Eu fazia os exercícios, e eles funcionavam, mas, por muito tempo, parecia que eu não entendia muito bem como o computador conseguia fazer a "mágica" de executar meus programas de forma mais "rápida".&lt;/p&gt;

&lt;p&gt;Este guia foi feito para abordar os conceitos de processos, clone de processos e threads, usando Ruby como linguagem base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby: Uma Linguagem Amigável e Elegante
&lt;/h2&gt;

&lt;p&gt;Ruby é uma linguagem de programação interpretada e multi paradigma, com tipagem dinâmica e gerenciamento automático de memória. O interpretador mais comum é o &lt;strong&gt;MRI&lt;/strong&gt;, sigla para &lt;em&gt;Matz's Ruby Interpreter&lt;/em&gt;, também conhecido como &lt;strong&gt;CRuby&lt;/strong&gt; por ser implementado em C. Ele é considerado o interpretador padrão da linguagem e o mais amplamente utilizado pela comunidade.&lt;/p&gt;

&lt;p&gt;MRI por padrão oferece suporte a tão falada concorrência.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recurso&lt;/th&gt;
&lt;th&gt;Suporte?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Threads&lt;/td&gt;
&lt;td&gt;✅ Sim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fork&lt;/td&gt;
&lt;td&gt;✅ Sim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Processos&lt;/td&gt;
&lt;td&gt;✅ Sim&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Vamos utilizar duas versões diferentes de interpretadores Ruby para os testes. Para alternar entre eles de forma fácil e rápida, usaremos o gerenciador de versões &lt;a href="https://github.com/rbenv/rbenv" rel="noopener noreferrer"&gt;rbenv&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Comandos para instalar as versões usadas aqui:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rbenv install 3.3.5
rbenv install jruby-9.4.8.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comando para usar uma versão especifica de forma global:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rbenv global 3.3.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comando para listar as versões instaladas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rbenv versions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saída&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  system
* 3.3.5 (set by /home/michael/.rbenv/version)
  jruby-9.4.8.0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Processos
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Usaremos o Ubuntu como sistema operacional, ou seja, os conceitos abordados aqui se aplicam a sistemas do tipo &lt;strong&gt;UNIX-like&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Os programas que rodam no seu Sistema operacional estão dentro de uma estrutura chamada &lt;strong&gt;processos&lt;/strong&gt;. Os processos são independentes entre si e tem seus próprios recursos, como espaço em memória. O sistema operacional é inteligente o suficiente para gerenciar esses processos de forma simultânea usando um escalonador preemptivo. O fato de cada processo ter seu próprio espaço na memória permite que o sistema operacional lide com eles de forma concorrente. Ele consegue fazer trocas de contexto (context switch), aumentando a eficiência no uso da CPU.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcbxo914i8ct3c7kjrma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcbxo914i8ct3c7kjrma.png" alt=" " width="441" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Exemplo: você está assistindo a um vídeo no YouTube, editando um texto no LibreOffice e recebendo mensagens no seu Telegram, cada tarefa dessa é um processo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sua CPU tem múltiplos &lt;strong&gt;núcleos&lt;/strong&gt;, mas você provavelmente tem dezenas de processos ativos. O sistema operacional faz a troca de contexto entre esses processos em questão de milissegundos, salvando o estado do processo atual e carregando o de outro.&lt;/p&gt;

&lt;p&gt;É possivel listar os processos ativos do seu sistema com o comando &lt;code&gt;top&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Segundo o manual (man top): The  top  program  provides  a  dynamic  real-time  view of a running system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cada processo possui um estado próprio e é identificado por um PID (&lt;em&gt;Process ID&lt;/em&gt;). O sistema operacional reconhece os processos como uma unidade de &lt;strong&gt;concorrência&lt;/strong&gt;. Os processos também podem se comunicar entre si por meio de IPC (&lt;em&gt;Inter-Process Communication&lt;/em&gt;), um conjunto de mecanismos que permite essa troca de informações.&lt;/p&gt;

&lt;p&gt;É possível criar um processo filho por meio da &lt;em&gt;syscall&lt;/em&gt; &lt;em&gt;&lt;strong&gt;clone&lt;/strong&gt;&lt;/em&gt; do sistema operacional.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O que é uma &lt;em&gt;syscall&lt;/em&gt;? é um mecanismo pelo qual um programa de computador solicita serviços ao kernel do sistema operacional no qual ele está sendo executado&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quando essa chamada é executada, o processo pai é clonado, e um novo processo filho é criado com uma cópia do seu espaço de memória, fila de execução e descritores de arquivos, tudo isso dependendo dos parâmetros passados à syscall.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Segue a documentação da syscall clone diretamente do manual Linux &lt;a href="https://man7.org/linux/man-pages/man2/clone3.2.html" rel="noopener noreferrer"&gt;https://man7.org/linux/man-pages/man2/clone3.2.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vamos criar um processo filho a partir de um processo pai utilizando &lt;a href="https://docs.ruby-lang.org/en/master/Process.html#module-Process-label-Process+Groups" rel="noopener noreferrer"&gt;fork&lt;/a&gt; em Ruby.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Processo pai: PID = &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fork&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Processo filho: PID = &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, PPID = &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ppid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Processo filho terminou."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Processo pai continua executando..."&lt;/span&gt;
&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Processo pai terminou após o filho."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saida&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Processo pai: PID &lt;span class="o"&gt;=&lt;/span&gt; 2864854
Processo pai continua executando...
Processo filho: PID &lt;span class="o"&gt;=&lt;/span&gt; 2864890, PPID &lt;span class="o"&gt;=&lt;/span&gt; 2864854
Processo filho terminou.
Processo pai terminou após o filho.
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O processo pai imprime seu próprio PID e, em seguida, cria um processo filho usando fork. O filho imprime seu PID e o do pai (PPID), simulando uma tarefa demorada com &lt;strong&gt;sleep&lt;/strong&gt;. Enquanto isso, o pai continua executando, aguarda a finalização do filho com &lt;em&gt;Process.wait&lt;/em&gt;, e só então encerra sua execução.&lt;/p&gt;

&lt;p&gt;É possivel que um processo filho se comunique com o processo principal através de mensagens (&lt;em&gt;pipe&lt;/em&gt;), no ruby conseguimos fazer essa comunicação usando &lt;a href="https://docs.ruby-lang.org/en/master/IO.html" rel="noopener noreferrer"&gt;&lt;em&gt;&lt;strong&gt;IO.pipe&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;

&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fork&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
  &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Olá do processo filho!"&lt;/span&gt;
  &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;span class="n"&gt;mensagem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Pai recebeu: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mensagem&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;

&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saída&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Pai recebeu: Olá &lt;span class="k"&gt;do &lt;/span&gt;processo filho!
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 2866592
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Threads
&lt;/h2&gt;

&lt;p&gt;Agora que falamos um pouco sobre processos, vamos falar das threads. Threads são como "partes" de um processo, elas compartilham os mesmos recursos como a memória com o processo principal e com outras threads do mesmo processo. Enquanto um processo é um contêiner que possui memória, recursos e ao menos uma &lt;em&gt;thread&lt;/em&gt;, as &lt;em&gt;threads&lt;/em&gt; são unidades de execução dentro desse contêiner, trabalhando de forma colaborativa no mesmo espaço de memória.&lt;/p&gt;

&lt;p&gt;As &lt;em&gt;threads&lt;/em&gt; são úteis quando queremos executar múltiplas tarefas simultaneamente dentro de um único processo, oferecendo uma forma leve e eficiente de alcançar a tão desejada concorrência.&lt;/p&gt;

&lt;p&gt;Vamos escrever um programa que cria 4 &lt;em&gt;threads&lt;/em&gt; que executam um cálculo de raiz quadrada.&lt;/p&gt;

&lt;p&gt;Cada &lt;em&gt;thread&lt;/em&gt; calcula a soma das raízes quadradas dos números de 0 até 99.999.999, totalizando &lt;strong&gt;400 milhões de operações&lt;/strong&gt; (Sim é bastante coisa). O programa utiliza a biblioteca &lt;em&gt;&lt;strong&gt;Benchmark&lt;/strong&gt;&lt;/em&gt; para medir e exibir o tempo total gasto para que todas as threads concluam o processamento.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'benchmark'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'date'&lt;/span&gt;

&lt;span class="no"&gt;NUM_THREADS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="no"&gt;WORK_PER_THREAD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100_000_000&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trabalho_pesado&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="no"&gt;WORK_PER_THREAD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Thread &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; terminou! &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Iniciando benchmark com &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;NUM_THREADS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; threads... em &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;tempo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;realtime&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;NUM_THREADS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Thread trabalhando: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="n"&gt;trabalho_pesado&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:join&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Tempo total: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;tempo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; segundos"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saída&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Iniciando benchmark com 4 threads... em 2025-05-18T12:15:31-03:00
Thread trabalhando: 31440
Thread trabalhando: 31460
Thread trabalhando: 31480
Thread trabalhando: 31500
Thread 31500 terminou! 2025-05-18T12:16:04-03:00
Thread 31500 terminou! 2025-05-18T12:16:05-03:00
Thread 31500 terminou! 2025-05-18T12:16:05-03:00
Thread 31500 terminou! 2025-05-18T12:16:05-03:00
Tempo total: 34.12 segundos
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nosso programa levou &lt;strong&gt;34,12&lt;/strong&gt; segundos para executar todas essas operações. Pode parecer rápido, mas ainda podemos melhorar. Nesse caso, usamos concorrência para dividir a carga em 4 &lt;em&gt;threads&lt;/em&gt;, que trabalham de forma concorrente, mas não em paralelo. Por que não em paralelo? Será que Ruby não escala?&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;MRI&lt;/strong&gt; utiliza um mecanismo chamado &lt;strong&gt;GIL&lt;/strong&gt; (&lt;em&gt;Global Interpreter Lock&lt;/em&gt;), que garante que apenas uma thread Ruby execute código Ruby por vez, mesmo em máquinas com múltiplos núcleos. Ou seja, o MRI não é &lt;em&gt;thread safe&lt;/em&gt; em nível interno para algumas operações. O GIL impede que múltiplas threads executem código Ruby simultaneamente, evitando problemas de concorrência no gerenciamento de memória e objetos.&lt;/p&gt;

&lt;p&gt;Agora, o que faremos é mudar o interpretador. Até aqui usamos o padrão MRI para os testes, mas vamos migrar para uma implementação que roda sobre a máquina virtual Java, o &lt;em&gt;&lt;strong&gt;JRuby&lt;/strong&gt;&lt;/em&gt;. Esse interpretador permite o paralelismo real entre threads, o que pode melhorar o desempenho nesse caso.&lt;/p&gt;

&lt;p&gt;Vamos mudar a versão do Ruby usando o gerenciador rbenv.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rbenv global jruby-9.4.8.0
ruby --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saída&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jruby 9.4.8.0 (3.1.4) 2024-07-02 4d41e55a67 OpenJDK 64-Bit Server VM 11.0.26+4-post-Ubuntu-1ubuntu122.04 on 11.0.26+4-post-Ubuntu-1ubuntu122.04 +jit [x86_64-linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora que mudamos o interpretador para &lt;strong&gt;JRuby&lt;/strong&gt;, vamos executar o mesmo código novamente. E o resultado é bem diferente...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Iniciando benchmark com 4 threads... em 2025-05-18T12:24:29-03:00
Thread trabalhando: 4004
Thread trabalhando: 4000
Thread trabalhando: 4008
Thread trabalhando: 4012
Thread 4012 terminou! 2025-05-18T12:24:43-03:00
Thread 4000 terminou! 2025-05-18T12:24:43-03:00
Thread 4008 terminou! 2025-05-18T12:24:43-03:00
Thread 4004 terminou! 2025-05-18T12:24:43-03:00
Tempo total: 13.48 segundos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nosso programa, interpretado pelo &lt;em&gt;JRuby&lt;/em&gt;, levou apenas &lt;strong&gt;13,48&lt;/strong&gt; segundos para realizar os 400 milhões de cálculos. Isso representa uma execução aproximadamente &lt;strong&gt;2,5 vezes&lt;/strong&gt; mais rápida, ou cerca de &lt;strong&gt;60,5% de ganho em desempenho&lt;/strong&gt; em comparação com o MRI. Essa diferença acontece porque o &lt;em&gt;JRuby&lt;/em&gt; consegue aproveitar os múltiplos núcleos do processador (A Java Virtual Machine é &lt;em&gt;thread safe&lt;/em&gt;), aplicando paralelismo real entre as threads do programa.&lt;/p&gt;

&lt;p&gt;Isso quer dizer que, para esse programa, precisamos mudar de interpretador para usar paralelismo? Não necessariamente. Podemos continuar usando o MRI e ainda assim aproveitar múltiplos núcleos através do uso de &lt;em&gt;fork&lt;/em&gt;. Isso porque o &lt;em&gt;fork&lt;/em&gt; cria um novo processo, independente do pai, com sua própria memória, seu próprio GIL e sua própria thread principal. Como cada processo é isolado, o sistema operacional pode executá-los em paralelo real, os distribuindo entre os núcleos da &lt;em&gt;CPU&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Vamos mudar para ruby padrão usando rbenv&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rbenv global 3.3.5
ruby --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saída&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [x86_64-linux]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos executar o programa usando fork&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'benchmark'&lt;/span&gt;

&lt;span class="no"&gt;NUM_PROCESSES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="no"&gt;WORK_PER_PROCESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100_000_000&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trabalho_pesado&lt;/span&gt;
  &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="no"&gt;WORK_PER_PROCESS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Iniciando benchmark com &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;NUM_PROCESSES&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; processos (fork)..."&lt;/span&gt;

&lt;span class="n"&gt;tempo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;realtime&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;pids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="no"&gt;NUM_PROCESSES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fork&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"[Filho #&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] PID: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Pai: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ppid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="n"&gt;trabalho_pesado&lt;/span&gt;
      &lt;span class="nb"&gt;exit!&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;pids&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;pids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Tempo total: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;tempo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; segundos"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saída&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Filho #0] PID: 2881532, Pai: 2881489
[Filho #1] PID: 2881535, Pai: 2881489
[Filho #2] PID: 2881538, Pai: 2881489
[Filho #3] PID: 2881541, Pai: 2881489
Tempo total: 8.74 segundos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No final esse foi o resultado dos nossos testes&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Teste&lt;/th&gt;
&lt;th&gt;Interpretador&lt;/th&gt;
&lt;th&gt;Abordagem&lt;/th&gt;
&lt;th&gt;Tempo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Teste 1&lt;/td&gt;
&lt;td&gt;MRI&lt;/td&gt;
&lt;td&gt;Threads&lt;/td&gt;
&lt;td&gt;34.12 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Teste 2&lt;/td&gt;
&lt;td&gt;JRuby&lt;/td&gt;
&lt;td&gt;Threads&lt;/td&gt;
&lt;td&gt;13.48 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Teste 3&lt;/td&gt;
&lt;td&gt;MRI&lt;/td&gt;
&lt;td&gt;Fork&lt;/td&gt;
&lt;td&gt;8.74 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Teste 3 foi ~&lt;strong&gt;3,9 vezes&lt;/strong&gt; mais rápido e teve um ganho de ~&lt;strong&gt;74,4%&lt;/strong&gt; sobre o Teste 1 e ~&lt;strong&gt;1,5 vezes&lt;/strong&gt; mais rápido e teve um ganho de ~&lt;strong&gt;35,2%&lt;/strong&gt; sobre o Teste 2.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Race condition
&lt;/h2&gt;

&lt;p&gt;Agora vamos falar sobre um problema bastante comum quando tratamos de concorrência, o &lt;em&gt;race condition&lt;/em&gt;. Sabemos que as threads são executadas de forma concorrente, mas como não controlamos a ordem em que elas são executadas, isso fica a cargo do escalonador preemptivo do sistema operacional. Ou seja, quando executamos uma tarefa com múltiplas threads que dependem da ordem de execução, temos o risco de enfrentar uma race condition. Para exemplificar, vamos rodar o código abaixo usando o Ruby padrão (MRI) e o JRuby, assim como nos exemplos anteriores. O programa cria 10 threads e cada uma incrementa a variável contador 1000 vezes. Esperamos que o valor final seja 10000.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;contador&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;contador&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:join&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"O valor final deve ser 10000"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Valor final do contador: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;contador&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saída usando MRI (Versão do ruby 3.3.5)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;O valor final deve ser 10000
Valor final do contador: 10000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saída usando JRuby (Versão jruby-9.4.8.0)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;O valor final deve ser 10000
Valor final do contador: 9451
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perceba que a saída com JRuby não retornou a contagem de 10000 e sim 9451, executando outra vez:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;O valor final deve ser 10000
Valor final do contador: 8800
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No MRI (CRuby), mesmo utilizando múltiplas threads nativas do sistema operacional, existe um bloqueio global chamado GVL (Global VM Lock) que garante que apenas uma thread execute código Ruby por vez na CPU, evitando assim condições de corrida (race conditions) em operações concorrentes sobre variáveis compartilhadas. Esse lock global limita o paralelismo de CPU, liberando-o apenas em operações de I/O ou chamadas de código nativo, o que explica por que o exemplo com múltiplas threads incrementando uma variável compartilhada sempre retorna o valor correto no MRI, ao contrário do que acontece em outras implementações como JRuby que não possuem esse bloqueio global.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Ruby é realmente fantástico, não é? Ele conta com gems (Gem são as bibliotecas de ruby) especializadas para paralelismo, como a gem &lt;a href="https://rubygems.org/gems/parallel/versions/1.11.2" rel="noopener noreferrer"&gt;parallel&lt;/a&gt;, que facilita o uso de múltiplos processos. Além disso, Ruby oferece APIs nativas para a criação de threads, que funcionam muito bem em tarefas de I/O concorrente. É importante lembrar que criar múltiplas threads ou usar fork pode aumentar a complexidade no gerenciamento dos recursos e do fluxo do programa, exigindo cuidado na implementação. Ou seja, não existe uma bala de prata para todas as situações, pois a melhor abordagem depende do contexto e dos objetivos do projeto. Por aqui aprendemos como Ruby lida com processos forkados e threads, a comunicação entre processos e entendemos o risco das race conditions, que acontecem quando recursos compartilhados são acessados sem a devida sincronização.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://concorrencia101.leandronsp.com/" rel="noopener noreferrer"&gt;https://concorrencia101.leandronsp.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.akitaonrails.com/2019/03/13/akitando-43-concorrencia-e-paralelismo-parte-1-entendendo-back-end-para-iniciantes-parte-3" rel="noopener noreferrer"&gt;https://www.akitaonrails.com/2019/03/13/akitando-43-concorrencia-e-paralelismo-parte-1-entendendo-back-end-para-iniciantes-parte-3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.toptal.com/ruby/ruby-concurrency-and-parallelism-a-practical-primer" rel="noopener noreferrer"&gt;https://www.toptal.com/ruby/ruby-concurrency-and-parallelism-a-practical-primer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidock.com/ruby/Process/fork/class" rel="noopener noreferrer"&gt;https://apidock.com/ruby/Process/fork/class&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rubyguides.com/2015/07/ruby-threads/" rel="noopener noreferrer"&gt;https://www.rubyguides.com/2015/07/ruby-threads/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linuxjourney-com.translate.goog/lesson/monitor-processes-ps-command?_x_tr_sl=en&amp;amp;_x_tr_tl=pt&amp;amp;_x_tr_hl=pt&amp;amp;_x_tr_pto=tc" rel="noopener noreferrer"&gt;https://linuxjourney-com.translate.goog/lesson/monitor-processes-ps-command?_x_tr_sl=en&amp;amp;_x_tr_tl=pt&amp;amp;_x_tr_hl=pt&amp;amp;_x_tr_pto=tc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://man7.org/linux/man-pages/man2/syscalls.2.html" rel="noopener noreferrer"&gt;https://man7.org/linux/man-pages/man2/syscalls.2.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>programming</category>
      <category>performance</category>
      <category>firstpost</category>
    </item>
  </channel>
</rss>
