Forking de processos

Para aqueles que já leram a parte I do guia, não terão qualquer dificuldade em entender os conceitos aqui abordados. E caso também você tenha real interesse em aprender sobre concorrência, certamente já leu como funciona forking de processos em C.

Em Ruby, existe um método especial chamado fork que, na hora de ser executado, faz a chamada de sistema fork.

Vamos ver o primeiro exemplo similar ao que vimos inicialmente no módulo de C:

forking.rb
fork
puts "Hello"

Agora vamos executar o programa utilizando o interpretador CRuby chamando ruby forking.rb , que imprime:

Hello
Hello

So far, so good. Já vimos anteriormente o motivo de aparecer Hello 2 vezes. Em um outro exemplo, podemos ver que o pid é diferente dependendo se estamos no parent ou no child:

pid = fork

if pid
  puts "In parent, pid is #{Process.pid}. Child is #{pid}"
else
  puts "In child, pid is #{Process.pid}"
end
In parent, pid is 77278. Child is 77430
In child, pid is 77430

Leiam a documentação, sempre

Um exemplo mais robusto

Agora, vamos a um exemplo um pouco mais robusto com o uso de fork de processos, onde um processo pai dispara 3 processos filhos que irão executar uma tarefa que demora 2 segundos cada:

forking.rb
def perform
  pid = Process.pid

  puts "Processo filho (PID: #{pid}) executando tarefa..."
  sleep(2) # Simula uma tarefa que leva 2 segundos
  puts "Processo filho (PID: #{pid}) completou a tarefa!"
end

wait_pids = []

# Criação dos processos filhos
3.times do
  pid = fork do
    perform # Código do processo filho
  end

  wait_pids << pid # Armazena o PID do filho para controle
end

# O processo pai aguarda cada filho terminar
wait_pids.each do |child_pid|
  Process.wait(child_pid)

  puts "Pai: Processo filho com PID #{child_pid} terminou."
end

puts "Pai: Todos os filhos terminaram. Finalizando."
Processo filho (PID: 78317) executando tarefa...
Processo filho (PID: 78319) executando tarefa...
Processo filho (PID: 78318) executando tarefa...
Processo filho (PID: 78318) completou a tarefa!
Processo filho (PID: 78317) completou a tarefa!
Processo filho (PID: 78319) completou a tarefa!
Pai: Processo filho com PID 78317 terminou.
Pai: Processo filho com PID 78318 terminou.
Pai: Processo filho com PID 78319 terminou.
Pai: Todos os filhos terminaram. Finalizando.

Interessante notar aqui:

  • os 3 processos filhos foram criados e iniciaram sua execução nesta ordem (78317, 78318, 78319)

  • entretanto, de acordo com as mensagens, o processo 78318 terminou antes dos outros 2

  • mas para o processo pai, o filho que terminou primeiro foi o 78317

Isto, senhoras e senhores, é a maravilha da concorrência. Não temos controle algum sobre a ordem e execução das tarefas!

Sim, vou repetir isso inúmeras vezes neste guia kk

Comunicação entre processos (IPC)

Assim como em C, podemos fazer IPC em Ruby utilizando pipes:

# Cria um pipe com dois descritores (leitura e escrita)
read_fd, write_fd = IO.pipe

pid = fork do
  # Processo filho
  read_fd.close # Fecha a extremidade de leitura no filho
  mensagem = "Message from child!"
  write_fd.puts(mensagem) # Escreve a mensagem no pipe
  write_fd.close # Fecha a extremidade de escrita no filho
end

# Processo pai
write_fd.close # Fecha a extremidade de escrita no pai
mensagem_recebida = read_fd.gets.chomp # Lê a mensagem do pipe
puts "Parent received message: #{mensagem_recebida}"
read_fd.close # Fecha a extremidade de leitura no pai

# Aguarda o processo filho terminar
Process.wait(pid)

Este é um exemplo bastante simples de como 2 processos distintos podem conversar entre si, através da utilização de UNIX pipes.

Repare como que até o momento, conseguimos fazer em Ruby tudo o que foi possível fazer em C com relação a forking de processos, e isto se deve ao fato de que o interpretador CRuby é escrito em C, o que torna simples a interface para as syscalls envolvidas no sistema operacional.

Agora, chegou o momento de vermos as Threads em Ruby.

Last updated