Hi Rubyists!!
Ruby contains two seemingly equivalents tools for thread
synchronization: Mutex (defined in thread.rb) and Monitor (defined in
monitor.rb). They both implement classic mutex and conditional
variable functionality
and have the same API. This begs two questions:
1: What is the difference between Monitor and Mutex?
The main difference is that Monitors are reentrant while Mutexes are not:
require 'monitor'
=> true
require 'thread'
=> true
m=Monitor.new
=> #<Monitor:0x101a6930 @mon_entering_queue=, @mon_count=0, @mon_waiting_queue=, @mon_owner=nil>
m.synchronize { m.synchronize { puts "foo" } }
foo
=> nil
m=Mutex.new
=> #<Mutex:0x10198f80 @locked=false, @waiting=>
m.synchronize { m.synchronize { puts "foo" } }
ThreadError: stopping only thread
note: use sleep to stop forever
from /usr/lib/ruby/1.8/thread.rb:100:in `stop'
from /usr/lib/ruby/1.8/thread.rb:100:in `lock'
from /usr/lib/ruby/1.8/thread.rb:133:in `synchronize'
from (irb):6
from /usr/lib/ruby/1.8/thread.rb:135:in `synchronize'
from (irb):6
2: Which one of the two is the preferred solution? PickAxe 1-st
edition covered Mutex, PickAxe 2-nd edition covers Monitor in main
text and Mutex in passing in library reference.
They are mostly equivalent. I guess it mainly boils down to this: if your code needs to be reentrant or if methods invoke each other that are synchronized on the same lock use Monitor. For all other cases or where performance is important use Mutex. (Disclaimer: I didn't benchmark the two but since the implementation of a reentrant lock is more complicated I guess Monitor has some overhead over Mutex.) Note also that there is MonitorMixin which basically allows do to something like
class Foo
include MonitorMixin
def do_it_safely()
synchronize { puts "thread safe code" }
end
end
instead of
class Foo
def initialize() @lock = Mutex.new end
def do_it_safely()
@lock.synchronize { puts "thread safe code" }
end
end
There are more thread control primitives which might help you depending on the problem you are trying to solve: ConditionVariable and Queue. I recommend to *not* use Thread.critical and Thread.exclusive (which uses #critical internally) for several reasons:
- they limit concurrency more than necessary there is only a single lock which will prevent all but one thread from executing
- I view them as quite low level functionality which is purely there to implement other synchronization features like Mutex, Monitor etc.
- Although they might not be deprecated when we have a Ruby version that supports native threads the effect of using this single process wide lock will be even more dramatic, because there will be even more unused resources when the lock is held - especially in multiprocessor environments.
Kind regards
robert
···
slonik.az@gmail.com wrote:
from :0