Possible Duplicate:
Does ruby have the Java equivalent of synchronize keyword?
In Java we can make a method 'synchronized' by just using the 'synchronized' keyword in the function definition.
How do we do it in Ruby?
Synchronize keyword is not present in Ruby. But you can wrap the method call to a Mutex
(a class representing a mutually exclusive lock, also called a semaphore).
You can create a new Mutex for the class you want to protect from race conditions and use it internally everywhere. Note that each piece code that reads/writes those variables must use the same Mutex instance to access the variables at all times, otherwise the lock can't protect you.
See http://apidock.com/ruby/Mutex
class Foo
def initialize
@my_mutex = Mutex.new
@my_val = 0 # should be private
end
def synchronize(&block)
# to see how it breaks without the Mutex:
# 1) comment this line
@my_mutex.synchronize(&block)
# 2) uncomment this line
# yield
end
def current_value
synchronize do
@my_val
end
end
def modify
# the value should be 0 before and 0 after, if the Mutex is used correctly
synchronize do
@my_val += 1
sleep 0.25
@my_val -= 1
sleep 0.25
end
end
end
foo = Foo.new
threads = []
# spawn N worker threads, all trying to change the same variables
threads += (1..4).map { |i|
Thread.new {
puts "thread [##{i}]: modifying"
foo.modify
}
}
# spawn the checking thread
threads << Thread.new {
# print the value twice as fast as the other threads are changing it, so we are more likely to stumble upon wrong state
(NUM_THREADS * 2).times {
puts "thread [check]: checking..."
raise if foo.current_value != 0 # locking failed, crash
sleep 0.25
}
}
threads.map { |t| t.join } # wait for all threads to finish
If you disable mutex in this example above, the program crashes right away, because the checking thread reads a wrong value. Note that the checking thread must use the mutex too, because otherwise it would still be able to read the value in the middle of change by other threads. Everyone must use the same mutex to access that variable.
I've added a method to mimic the synchronized
keyword for that class. You could make a module from that and include it anywhere, but it's probably clearer to the reader to just add the Mutex variable and call it's synchronize
method directly.
Performance notes: Keep in mind that using locks limits the code wrapped in a synchronize
block to a single thread, which makes it slower. As Kevlin Henney said in one of his presentations, "[mutex] should've been called a 'bottleneck'".
The performance of the code using threads depends on your Ruby implementation (green vs native threads, etc.), the number of threads you use, and the number of your CPU cores. I/O operations can get away with much more threads than computationally intensive operations. It's better to use the same number of threads as you have CPU cores for those.