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:
We define
ReadOp
andWriteOp
structs to encapsulate read and write operations.The
StateManager
class manages the state with thread-safe read and write operations using a mutex.We create queues for read and write operations, and start a thread to process these operations.
We start 100 threads that continuously perform read operations and 10 threads that perform write operations.
Each operation is pushed to the appropriate queue and waits for the result.
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.