True parallelism in Ruby

Prerequisites

You need ruby 3.0.0 or later for this, if it’s not available yet, use the docker image:

docker run --rm -it ruby:3.0.0-rc1-alpine irb

Ractor

Here is a snippet that showcases everything you need to know in practice:

receiver = Ractor.new do
  while (result = receive) != :end
    puts result.inspect
  end
end

sender = Ractor.new(receiver) do |printer|
  printer.send({a: 1, b: 2})
  printer.send('some string')

  printer.send([1, 2, 3], move: true)
end

sender.take

receiver.send(:end)
receiver.take

Key points:

  • The sender and receiver blocks run in parallel.
  • Wait for a ractor to finish with #take.
  • Send sharable objects with #send.
  • Don’t reference global state, pass it around on the constructor (#new) or through #send.
  • Remember: if your object is not deeply frozen, it will be cloned, so either send small objects infrequently or prepare for memory bloat. Always benchmark and measure!

Full documentation here:

Other

There’s also…

… but nobody cares about these anymore, they’re old tech now. Evolve with the times or be left behind!