Bird
Raised Fist0
Rubyprogramming~10 mins

Ractor for true parallelism in Ruby - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Ractor for true parallelism
Create Ractor
→Ractor runs code in parallel
↓
Main thread continues
↓
Send message to Ractor
↓
Ractor processes message
↓
Ractor sends result back
↓
Main thread receives result
↓
Ractor ends or waits for more messages
Ractor creates a parallel thread that runs code independently, communicates via messages, and allows true parallelism.
Execution Sample
Ruby
r = Ractor.new do
  msg = Ractor.receive
  msg * 2
end

r.send(10)
result = r.take
This code creates a Ractor that doubles a received number and sends back the result.
Execution Table
StepActionRactor StateMain Thread StateMessage PassingOutput
1Create RactorWaiting for messageRunning main codeNo message yetNone
2Main thread sends 10Received message 10Waiting for resultMessage 10 sent to RactorNone
3Ractor processes messageComputing 10 * 2 = 20Waiting for resultProcessing messageNone
4Ractor sends resultResult 20 readyWaiting to receiveResult 20 sent to main threadNone
5Main thread receives resultIdle or waitingReceived 20Result 20 received20
6Program ends or Ractor waitsIdle or terminatedProgram endsNo messages20
💡 Execution stops after main thread receives the doubled result and program ends.
Variable Tracker
VariableStartAfter Step 2After Step 3After Step 5Final
rRactor object createdRactor received 10Ractor computed 20Ractor idleRactor idle or terminated
msgNone1010NoneNone
resultNoneNoneNone2020
Key Moments - 3 Insights
Why does the main thread not wait immediately after creating the Ractor?
Because the Ractor runs in parallel, the main thread continues until it explicitly waits for a result (see step 2 and 5 in execution_table).
How does the main thread get the result from the Ractor?
The main thread uses r.take to receive the message sent back by the Ractor (step 5 in execution_table).
What happens if the Ractor does not receive a message?
The Ractor waits (blocks) at Ractor.receive until a message arrives (step 1 in execution_table).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, what is the Ractor state right after the main thread sends 10?
AWaiting for message
BComputing 10 * 2 = 20
CReceived message 10
DIdle or terminated
💡 Hint
Check step 2 in the execution_table under 'Ractor State'.
At which step does the main thread receive the result from the Ractor?
AStep 5
BStep 4
CStep 3
DStep 6
💡 Hint
Look for 'Main thread receives result' in the execution_table.
If the main thread sends 20 instead of 10, what would change in the execution_table?
AThe Ractor would not receive any message
BThe Ractor state at step 3 would show computing 20 * 2 = 40
CThe main thread would receive 10 at step 5
DThe program would end earlier
💡 Hint
Check how the message value affects the computation in step 3.
Concept Snapshot
Ractor.new { code } creates a parallel Ractor.
Use r.send(msg) to send messages.
Use Ractor.receive inside Ractor to get messages.
Use r.take to get messages from Ractor.
Ractors run truly in parallel, no shared memory.
Communication only via messages.
Full Transcript
This example shows how Ruby's Ractor enables true parallelism by running code in a separate thread. The main thread creates a Ractor that waits for a message, doubles it, and sends back the result. The main thread sends the number 10, the Ractor receives it, computes 20, and sends it back. The main thread then receives 20 and continues. Variables like 'r', 'msg', and 'result' change states as messages pass. Key points include that the main thread does not block on Ractor creation, communication is via messages, and Ractor waits for messages if none are sent. The quiz tests understanding of Ractor states and message passing.

Practice

(1/5)
1. What is the main purpose of using Ractor in Ruby?
easy
A. To run code truly in parallel on multiple CPU cores
B. To create graphical user interfaces
C. To handle database connections
D. To write single-threaded scripts

Solution

  1. Step 1: Understand Ractor's role

    Ractor is designed to enable true parallel execution in Ruby, using multiple CPU cores.
  2. Step 2: Compare options

    Options A, B, and C do not relate to parallelism or CPU core usage.
  3. Final Answer:

    To run code truly in parallel on multiple CPU cores -> Option A
  4. Quick Check:

    Ractor = true parallelism [OK]
Hint: Ractor means real parallel tasks in Ruby [OK]
Common Mistakes:
  • Confusing Ractor with threads
  • Thinking Ractor is for UI or database
  • Assuming Ractor runs code sequentially
2. Which of the following is the correct way to create a new Ractor in Ruby?
easy
A. Ractor.new { puts 'Hello from Ractor' }
B. Thread.new { puts 'Hello from Ractor' }
C. Process.spawn { puts 'Hello from Ractor' }
D. Fiber.new { puts 'Hello from Ractor' }

Solution

  1. Step 1: Identify Ractor creation syntax

    Ractor is created using Ractor.new with a block of code to run in parallel.
  2. Step 2: Eliminate other options

    Thread, Process, and Fiber are different concurrency tools, not Ractor.
  3. Final Answer:

    Ractor.new { puts 'Hello from Ractor' } -> Option A
  4. Quick Check:

    Use Ractor.new to create Ractors [OK]
Hint: Ractor.new starts a parallel task [OK]
Common Mistakes:
  • Using Thread.new instead of Ractor.new
  • Confusing Process.spawn with Ractor
  • Using Fiber.new which is cooperative, not parallel
3. What will be the output of this Ruby code using Ractor?
r = Ractor.new { 5 * 5 }
result = r.take
puts result
medium
A. RactorError
B. 5 * 5
C. nil
D. 25

Solution

  1. Step 1: Understand Ractor block execution

    The block { 5 * 5 } runs inside the Ractor and returns 25.
  2. Step 2: Use take to get the result

    r.take receives the value returned by the Ractor block, which is 25.
  3. Final Answer:

    25 -> Option D
  4. Quick Check:

    Ractor returns 25, take receives it [OK]
Hint: Ractor block returns value, take fetches it [OK]
Common Mistakes:
  • Expecting string '5 * 5' instead of calculation
  • Confusing take with yield
  • Assuming nil or error without communication
4. Identify the error in this Ractor code snippet:
r = Ractor.new do
  Ractor.yield 10
  20
end
puts r.take
puts r.take
medium
A. Ractor.new requires arguments
B. Second r.take call will cause Ractor::Closed error
C. No error, code runs fine
D. Calling Ractor.yield inside Ractor block is invalid

Solution

  1. Step 1: Understand Ractor.yield and r.take

    Ractor.yield sends a value to the main thread, and r.take receives it. The first r.take gets 10.
  2. Step 2: Analyze second r.take call

    The block returns 20 after yield, which is the final value. The second r.take gets 20. After that, the Ractor is closed. A third r.take would cause error, but here only two calls are made, so no error.
  3. Step 3: Check the code carefully

    Both r.take calls are valid; no error occurs.
  4. Final Answer:

    No error, code runs fine -> Option C
  5. Quick Check:

    Ractor.yield and take work twice safely [OK]
Hint: Ractor.yield sends values, take receives them sequentially [OK]
Common Mistakes:
  • Thinking Ractor.yield is invalid inside Ractor
  • Expecting error on second take call
  • Assuming Ractor.new needs arguments
5. You want to sum numbers from 1 to 100 using two Ractors running in parallel, each summing half the range. Which code correctly implements this?
hard
A. r1 = Ractor.new { (1..100).sum } r2 = Ractor.new { (1..100).sum } puts r1.take + r2.take
B. r1 = Ractor.new { (1..50).sum } r2 = Ractor.new { (51..100).sum } puts r1.take + r2.take
C. r1 = Thread.new { (1..50).sum } r2 = Thread.new { (51..100).sum } puts r1.value + r2.value
D. r1 = Ractor.new { (1..50).reduce(:+) } r2 = Ractor.new { (51..100).reduce(:+) } puts r1.take * r2.take

Solution

  1. Step 1: Split the range correctly

    We want two Ractors, one summing 1 to 50, the other 51 to 100.
  2. Step 2: Check the operations and combining results

    r1 = Ractor.new { (1..50).sum } r2 = Ractor.new { (51..100).sum } puts r1.take + r2.take sums each half and adds results correctly. r1 = Ractor.new { (1..100).sum } r2 = Ractor.new { (1..100).sum } puts r1.take + r2.take sums full range twice (wrong). r1 = Thread.new { (1..50).sum } r2 = Thread.new { (51..100).sum } puts r1.value + r2.value uses Thread, not Ractor. r1 = Ractor.new { (1..50).reduce(:+) } r2 = Ractor.new { (51..100).reduce(:+) } puts r1.take * r2.take multiplies sums instead of adding.
  3. Final Answer:

    r1 = Ractor.new { (1..50).sum } r2 = Ractor.new { (51..100).sum } puts r1.take + r2.take -> Option B
  4. Quick Check:

    Split range, sum halves, add results [OK]
Hint: Split work, sum halves in Ractors, then add [OK]
Common Mistakes:
  • Using Thread instead of Ractor
  • Summing full range twice
  • Multiplying sums instead of adding
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard