Stateful Goroutines in Ruby

In this example, we’ll demonstrate how to manage state using threads and a shared data structure in Ruby. This approach aligns with Ruby’s concurrency model using threads and synchronization mechanisms.

require 'thread'

# These classes encapsulate read and write operations
ReadOp = Struct.new(:key, :result)
WriteOp = Struct.new(:key, :val, :result)

# Create a class to manage the state
class StateManager
  def initialize
    @state = {}
    @mutex = Mutex.new
  end

  def read(key)
    @mutex.synchronize { @state[key] }
  end

  def write(key, value)
    @mutex.synchronize { @state[key] = value }
    true
  end
end

# Initialize counters for operations
read_ops = write_ops = 0

# Create a state manager
state_manager = StateManager.new

# Create queues for read and write operations
reads = Queue.new
writes = Queue.new

# Start a thread to process read and write operations
Thread.new do
  loop do
    Thread.pass
    operation = Thread.select([reads, writes])[0][0].pop
    case operation
    when ReadOp
      operation.result.push(state_manager.read(operation.key))
    when WriteOp
      operation.result.push(state_manager.write(operation.key, operation.val))
    end
  end
end

# Start 100 threads to perform read operations
100.times do
  Thread.new do
    loop do
      read_op = ReadOp.new(rand(5), Queue.new)
      reads.push(read_op)
      read_op.result.pop
      read_ops += 1
      sleep(0.001)
    end
  end
end

# Start 10 threads to perform write operations
10.times do
  Thread.new do
    loop do
      write_op = WriteOp.new(rand(5), rand(100), Queue.new)
      writes.push(write_op)
      write_op.result.pop
      write_ops += 1
      sleep(0.001)
    end
  end
end

# Let the threads work for a second
sleep(1)

# Report the operation counts
puts "readOps: #{read_ops}"
puts "writeOps: #{write_ops}"

This Ruby code demonstrates state management using threads and a shared data structure. Here’s a breakdown of what’s happening:

  1. We define ReadOp and WriteOp structs to encapsulate read and write operations.

  2. The StateManager class manages the state with thread-safe read and write operations using a mutex.

  3. We create queues for read and write operations, and start a thread to process these operations.

  4. We start 100 threads that continuously perform read operations and 10 threads that perform write operations.

  5. Each operation is pushed to the appropriate queue and waits for the result.

  6. After letting the threads run for a second, we report the total number of read and write operations performed.

To run the program, save it to a file (e.g., stateful_threads.rb) and use the Ruby interpreter:

$ ruby stateful_threads.rb
readOps: 71708
writeOps: 7177

This thread-based approach in Ruby is analogous to the goroutine-based approach in the original example. It provides a way to manage shared state safely across multiple threads. While it might be more complex than using a simple mutex, it can be useful in scenarios involving multiple synchronization points or when dealing with more complex concurrency patterns.

Remember to choose the approach that makes your program easiest to understand and reason about in terms of correctness and performance.