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:
=3D> #<Monitor:0x101a6930 @mon_entering_queue=3D[], @mon_count=3D0,
@mon_waiting_queue=3D[], @mon_owner=3Dnil>foo
=3D> nilThreadError: 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
from :0
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 you= r
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 gu= ess
Monitor has some overhead over Mutex.) Note also that there is MonitorMi= xin
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 =3D 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 o= n
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 resour= ces
when the lock is held - especially in multiprocessor environments.
Kind regards
robert