Concurrent Ruby with Ractors and Async

Sarah Mitchell Feb 2026
2 tabs
# Basic Ractor usage
ractor = Ractor.new do
  # This runs in parallel, isolated from main thread
  result = heavy_computation
  result
end

# Wait for result
result = ractor.take  # Blocks until ractor finishes

# Passing data to ractors
ractor = Ractor.new(10) do |n|
  n ** 2
end

ractor.take  # => 100

# Communication via messages
ractor = Ractor.new do
  loop do
    message = Ractor.receive
    puts "Received: #{message}"

    break if message == :stop
  end
end

ractor.send("Hello")
ractor.send("World")
ractor.send(:stop)

# Parallel processing with multiple ractors
def parallel_map(array)
  ractors = array.map do |item|
    Ractor.new(item) do |value|
      # CPU-intensive operation
      expensive_computation(value)
    end
  end

  ractors.map(&:take)
end

results = parallel_map([1, 2, 3, 4, 5])

# Worker pool pattern
class RactorPool
  def initialize(size)
    @queue = Ractor.new do
      jobs = []
      loop do
        Ractor.yield(jobs.shift) while jobs.any?
        jobs << Ractor.receive
      end
    end

    @workers = size.times.map do
      Ractor.new(@queue) do |queue|
        loop do
          job = queue.take
          result = job.call
          Ractor.yield(result)
        end
      end
    end
  end

  def submit(job)
    @queue.send(job)
  end

  def results
    @workers.map { |w| w.take }
  end
end

pool = RactorPool.new(4)
10.times { |i| pool.submit(-> { i ** 2 }) }
pool.results

# Ractor limitations (by design for safety)
# Cannot share mutable objects
data = "hello"
Ractor.new(data) do |str|
  str.upcase!  # Error! Cannot modify shared object
end

# Must freeze or copy data
data = "hello".freeze
Ractor.new(data) do |str|
  str.upcase  # OK - frozen string
end

# Only shareable objects can be passed:
# - Immutable objects (numbers, symbols, true, false, nil)
# - Frozen objects
# - Ractor objects themselves
# - Classes/modules
2 files · ruby Explain with highlit

Ruby 3+ introduces Ractors for true parallelism without GIL limitations. Ractors are isolated actors—no shared mutable state. I use Ractors for CPU-intensive parallel processing. Messages pass between Ractors via send and receive. Async gem provides structured concurrency—fibers for non-blocking I/O. Concurrent-ruby gem offers thread-safe primitives—Atomic, ThreadPool, Future, Promise. Understanding Ruby's GIL limits thread parallelism helps choose right concurrency tool. Ractors excel for parallel computations; fibers for I/O concurrency. Thread safety requires careful synchronization. Mutex guards shared state. Testing concurrent code needs deterministic fixtures. Concurrency unlocks Ruby's full CPU potential.