Skip to Content
Course content

100: Ractor for True Parallelism

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Wait, I thought Ruby had threads. Why do I need Ractors?

Here is the short version: Ruby threads are great for waiting on a database or an API call, but they're useless for heavy math. This is because of the GVL (Global VM Lock). Even if you have a 16-core processor, the GVL ensures that only one thread is actually executing Ruby code at any given moment. You're basically just switching between tasks very quickly, not doing them at the same time.

Ractors (short for "Ruby Actors") change the game by giving each Ractor its own GVL. If you spin up four Ractors on a four-core machine, you are finally utilizing 100% of your CPU. I usually tell people to think of Threads as "concurrency" (managing many things at once) and Ractors as "parallelism" (doing many things at once).

How do I actually send work to a Ractor and get a result back?

You don't share variables with a Ractor; you communicate with it. Think of a Ractor like a separate little person in a different room. You send them a message, they do the work, and they send a message back. You use Ractor.new to create one, and ractor.take to wait for the result.

Let's say we have a massive array of numbers and we want to calculate the sum of squares. Doing this in one thread is slow. Here is how we'd split it across two Ractors:

numbers = (1..10_000_000).to_a
half = numbers.size / 2

# We split the work into two Ractors
r1 = Ractor.new(numbers[0...half]) do |slice|
  slice.sum { |n| n * n }
end

r2 = Ractor.new(numbers[half..-1]) do |slice|
  slice.sum { |n| n * n }
end

# .take blocks the main thread until the Ractor returns a value
total = r1.take + r2.take
puts "The total is #{total}"

Why am I getting "can't share this object" errors?

This is the part that trips everyone up. To prevent the "race conditions" that make multi-threaded programming a nightmare, Ruby strictly forbids Ractors from sharing mutable objects. If you try to access a global variable or a string from the main thread inside a Ractor, Ruby will throw a Ractor::IsolationError.

You have two choices here. First, you can pass the data in as an argument to Ractor.new (which Ruby handles by moving or copying the object). Second, if you have a large configuration hash or a lookup table that many Ractors need to read, you can make it "shareable" using Ractor.make_shareable. This effectively freezes the object so it can't be changed, making it safe for every Ractor to look at simultaneously.

# This would normally fail if accessed inside a Ractor
CONFIG = { timeout: 30, retry_limit: 3, mode: :production }
Ractor.make_shareable(CONFIG)

r = Ractor.new do
  # Because we called make_shareable, this is allowed
  puts "The current mode is #{CONFIG[:mode]}"
end
r.take

I'll be honest: the restriction is annoying at first, but it's a lifesaver. It forces you to design your system so that data flows in one direction, which means you won't spend three days debugging a random crash caused by two threads editing the same string at the same millisecond.




📋 Practical Task

Parallel Image Metadata Processor

You have a list of 10 simulated "large image files" (represented as large strings or arrays of bytes). Writing a processor that analyzes these one-by-one is too slow. Your task is to build a MetadataAnalyzer that utilizes Ractors to process these files in parallel.

  • Create an array of 10 large strings (simulating file data).
  • Create a "shareable" configuration hash that contains a version number and a threshold value.
  • Spawn 5 Ractors. Each Ractor should take a portion of the files, read the shared configuration, and return the "checksum" (e.g., the sum of the bytes/characters) of the files it processed.
  • Collect the results using .take and print the final aggregate checksum.

Make sure you handle the object isolation correctly, or your program will crash before it even starts processing!

Rating
0 0

There are no comments for now.

to be the first to leave a comment.