Thread synchronization: Mutex or Monitor??

Discussion in 'Ruby' started by Guest, Dec 26, 2005.

  1. Guest

    Guest Guest

    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?

    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.

    Any opinions are highly appreciated.

    Thanks,
    --Leo--
    Guest, Dec 26, 2005
    #1
    1. Advertising

  2. On Mon, Dec 26, 2005 at 11:05:25PM +0900, wrote:
    } 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?
    }
    } 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.
    }
    } Any opinions are highly appreciated.

    There are at least three (maybe only three -- it's been a long time since
    my operating systems course in college) equivalently powerful concurrency
    control primitives: mutexes, monitors, and semaphores. They are
    equivalently powerful because you can implement any one of them in terms of
    any other. The implication, then, is that you should use the primitive that
    best suits your application. Here is some guidance on where each applies
    well:

    1) Semaphores involve counting, so they are typically used for controlling
    access to a limited, but plural, number of connections to some resource.
    Some good examples are audio channels or IO channels.

    2) Monitors are an OO construct and work well with controlling concurrent
    access to the multiple entry points in an object. A good example might
    be a shared queue object, on which the enqueue and dequeue operations
    are protected.

    3) Mutexes are the simplest primitives. They are best for protecting either
    a single entry point (i.e. a single function/method/block) or global
    data. An example might be a thread-safe printf function that prevents
    interleaved printing.

    } Thanks,
    } --Leo--
    --Greg
    Gregory Seidman, Dec 26, 2005
    #2
    1. Advertising

  3. Guest

    Guest Guest

    Gregory,
    many thanks for your reply and insightful description of general
    concepts of semaphores, mutexes and monitors. As far as I understand,
    Ruby implementation of monitor and mutex provide identical
    functionality that in your classification would be combination of your
    items 2) and 3).
    Ruby Mutex is quite advanced, pure OO and even can be mixed into any
    object (require "mutex_m.rb") exactly the same way as Ruby Monitor.=20
    So, in terms of their functionality and usage they can do the same
    things with the same API. Mutex#synchronize allows for control of
    multiple access points the same way as Mutex#synchronize does.

    --Leo--

    On 12/26/05, Gregory Seidman <> wrote:
    > On Mon, Dec 26, 2005 at 11:05:25PM +0900, wrote:
    > } Hi Rubyists!!
    > } Ruby contains two seemingly equivalents tools for thread synchronizatio=

    n:
    > } 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?
    > design}
    > } 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.
    > }
    > } Any opinions are highly appreciated.
    >
    > There are at least three (maybe only three -- it's been a long time since
    > my operating systems course in college) equivalently powerful concurrency
    > control primitives: mutexes, monitors, and semaphores. They are
    > equivalently powerful because you can implement any one of them in terms =

    of
    > any other. The implication, then, is that you should use the primitive th=

    at
    > best suits your application. Here is some guidance on where each applies
    > well:
    >
    > 1) Semaphores involve counting, so they are typically used for controllin=

    g
    > access to a limited, but plural, number of connections to some resourc=

    e.
    > Some good examples are audio channels or IO channels.
    >
    > 2) Monitors are an OO construct and work well with controlling concurrent
    > access to the multiple entry points in an object. A good example might
    > be a shared queue object, on which the enqueue and dequeue operations
    > are protected.
    >
    > 3) Mutexes are the simplest primitives. They are best for protecting eith=

    er
    > a single entry point (i.e. a single function/method/block) or global
    > data. An example might be a thread-safe printf function that prevents
    > interleaved printing.
    >
    > } Thanks,
    > } --Leo--
    > --Greg
    >
    >
    >
    Guest, Dec 26, 2005
    #3
  4. Guest

    Bill Kelly Guest

    From: "Gregory Seidman" <>
    >
    > There are at least three (maybe only three -- it's been a long time since
    > my operating systems course in college) equivalently powerful concurrency
    > control primitives: mutexes, monitors, and semaphores. They are
    > equivalently powerful because you can implement any one of them in terms of
    > any other. The implication, then, is that you should use the primitive that
    > best suits your application. Here is some guidance on where each applies
    > well:
    >
    > 1) Semaphores involve counting, so they are typically used for controlling
    > access to a limited, but plural, number of connections to some resource.
    > Some good examples are audio channels or IO channels.
    >
    > 2) Monitors are an OO construct and work well with controlling concurrent
    > access to the multiple entry points in an object. A good example might
    > be a shared queue object, on which the enqueue and dequeue operations
    > are protected.
    >
    > 3) Mutexes are the simplest primitives. They are best for protecting either
    > a single entry point (i.e. a single function/method/block) or global
    > data. An example might be a thread-safe printf function that prevents
    > interleaved printing.


    Also, Critical Sections.

    In Ruby,

    Thread.exclusive do
    # No other ruby threads will be scheduled while we're in this
    # Critical Section. (With a few exceptions, such as if we were
    # to sleep().)
    end


    Regards,

    Bill
    Bill Kelly, Dec 26, 2005
    #4
  5. Forwardable -- :[] -- :mad:what

    Reading about Forwardable I was shocked to see strange symbol-looking =
    notations I'd never
    seen before. Nothing the pickaxe led me to believe that :[] or =
    :mad:variable was valid Ruby.
    I've since figured out, I think, what it must mean but I'm wondering: =
    What other crazy
    syntax am I going to have to figure out? And does :[] simply name a =
    function, as I think?

    Will I be seeing :&, :**, :) and so on? Or even :) ?

    Shocked, simply shocked...

    Warren Seltzer
    Warren Seltzer, Dec 26, 2005
    #5
  6. Re: Forwardable -- :[] -- :mad:what

    On Dec 26, 2005, at 5:12 PM, Warren Seltzer wrote:

    > Reading about Forwardable I was shocked to see strange symbol-
    > looking notations I'd never
    > seen before. Nothing the pickaxe led me to believe that :[]
    > or :mad:variable was valid Ruby.
    > I've since figured out, I think, what it must mean but I'm
    > wondering: What other crazy
    > syntax am I going to have to figure out? And does :[] simply name
    > a function, as I think?
    >
    > Will I be seeing :&, :**, :) and so on? Or even :) ?
    >
    > Shocked, simply shocked...


    Symbols are really just immutable Strings. Not too much magic
    there. By convention, we generally use them to refer to method names
    and the like inside our programs (i.e. :each instead of "each").
    That's just because they are easier to type, faster in performance,
    and we won't need all of String's helper methods for them.

    Hope that helps explain what you are seeing.

    James Edward Gray II
    James Edward Gray II, Dec 26, 2005
    #6
  7. Re: Forwardable -- :[] -- :mad:what

    Warren Seltzer wrote:
    > Reading about Forwardable I was shocked to see strange symbol-looking
    > notations I'd never
    > seen before. Nothing the pickaxe led me to believe that :[] or
    > :mad:variable was valid Ruby.
    > I've since figured out, I think, what it must mean but I'm wondering:
    > What other crazy
    > syntax am I going to have to figure out? And does :[] simply name a
    > function, as I think?


    :<something> just indicates the variable being a Symbol,
    just like the quotes in 'something' indicates that variable
    to be a String. A Symbol does not intrinsically refer to a
    method, it is just a name (you could just as well use a String):

    foo.send :foobar_method # is equivalent to
    foo.send 'foobar_method'

    So, conceptually, :[] is more or less the same as '[]'
    and it may, indeed, be good to think of a Symbol as a
    kind of an immutable String :)

    > Will I be seeing :&, :**, :) and so on? Or even :) ?


    Well, :)-) is valid as the minus operator..

    > Shocked, simply shocked...


    Ha! Wait until you start constructing Symbols using the
    implicit String conversion:

    :'This is a Symbol too'

    > Warren Seltzer



    E

    --
    Posted via http://www.ruby-forum.com/.
    Eero Saynatkari, Dec 26, 2005
    #7
  8. wrote:
    > 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
    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 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
    Robert Klemme, Dec 27, 2005
    #8
  9. Guest

    Guest Guest

    Robert,
    thanks a lot for pointing out that Ruby Mutex is _not_ reentrant while
    Monitor is reentrant. Unfortunately, Ruby documentation (neither Rdoc
    nor PickAxe book) does not mention this important difference.

    Best Regards,
    --Leo--

    On 12/27/05, Robert Klemme <> wrote:
    > wrote:
    > > 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'

    > =3D> true
    > >> require 'thread'

    > =3D> true
    > >> m=3DMonitor.new

    > =3D> #<Monitor:0x101a6930 @mon_entering_queue=3D[], @mon_count=3D0,
    > @mon_waiting_queue=3D[], @mon_owner=3Dnil>
    > >> m.synchronize { m.synchronize { puts "foo" } }

    > foo
    > =3D> nil
    > >> m=3DMutex.new

    > =3D> #<Mutex:0x10198f80 @locked=3Dfalse, @waiting=3D[]>
    > >> 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
    > 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
    >
    >
    >
    Guest, Dec 27, 2005
    #9
  10. Guest

    Bill Kelly Guest

    From: "Robert Klemme" <>
    >
    > 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.


    Agreed, with regard to multiprocessor environments. With ruby's
    current implementation, using Thread.exclusive _where appropriate_
    seems perfectly reasonable to me. Grep ruby's standard library,
    you'll see several instances of its use.

    For instance drb/drb.rb DRbServer#initialize does the following:

    Thread.exclusive do
    DRb.primary_server = self unless DRb.primary_server
    end

    Yes, that could have been done with a Mutex, but why? Calling
    Mutex#synchronize (which in turn calls Mutex#lock and Mutex#unlock)
    involves considerably more code executed inside Thread.critical blocks
    than that one-liner above.

    So if the argument is that Thread.critical is prohibitively costly,
    one had better bear that in mind when calling any methods on Mutex.

    In the current implementation, each call to Mutex#synchronize
    involves around ten or so lines of ruby executed in at least
    two separate Thread.critical blocks (one of which is in a loop.)

    I agree that the relative costs of all these operations may change
    when we get to YARV + native threads + fine-grained locking that
    allows multiple ruby threads simultaneously executing on multiple
    processors. I wouldn't be surprised if Ruby's current thread.rb
    implementation had to be rewritten for that new system.

    But in any case, with ruby's current green threads, Thread.exclusive
    is about as efficient as you can get, for simple cases like the
    DRb one shown above. All it does is extend the current thread's
    quantum, effectively, for a brief period of time. There's no
    waiting involved.


    Regards,

    Bill
    Bill Kelly, Dec 27, 2005
    #10
  11. Bill Kelly <> wrote:
    > From: "Robert Klemme" <>
    >>
    >> 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.

    >
    > Agreed, with regard to multiprocessor environments. With ruby's
    > current implementation, using Thread.exclusive _where appropriate_
    > seems perfectly reasonable to me. Grep ruby's standard library,
    > you'll see several instances of its use.
    >
    > For instance drb/drb.rb DRbServer#initialize does the following:
    >
    > Thread.exclusive do
    > DRb.primary_server = self unless DRb.primary_server
    > end
    >
    > Yes, that could have been done with a Mutex, but why? Calling
    > Mutex#synchronize (which in turn calls Mutex#lock and Mutex#unlock)
    > involves considerably more code executed inside Thread.critical blocks
    > than that one-liner above.
    >
    > So if the argument is that Thread.critical is prohibitively costly,
    > one had better bear that in mind when calling any methods on Mutex.
    >
    > In the current implementation, each call to Mutex#synchronize
    > involves around ten or so lines of ruby executed in at least
    > two separate Thread.critical blocks (one of which is in a loop.)
    >
    > I agree that the relative costs of all these operations may change
    > when we get to YARV + native threads + fine-grained locking that
    > allows multiple ruby threads simultaneously executing on multiple
    > processors. I wouldn't be surprised if Ruby's current thread.rb
    > implementation had to be rewritten for that new system.


    I expect exactly that to happen - even more so, these classes are likely
    candidates for an implementation in the runtime system (similarly to Java's
    approach).

    > But in any case, with ruby's current green threads, Thread.exclusive
    > is about as efficient as you can get, for simple cases like the
    > DRb one shown above. All it does is extend the current thread's
    > quantum, effectively, for a brief period of time. There's no
    > waiting involved.


    I still don't like this approach even if the std lib does it. In an
    application you'll usually want more fine grained locks than process wide
    locks. IMHO it's not only a question of efficiency but also of design. I
    prefer to start with the cleaner design and only change to Thread.exclusive
    if there is a real need for this. I guess you know the quote about
    premature optimization... Of course you can view the usage of several lower
    grained locks vs. Thread.exclusive as an optimization, too (increases
    throughput). :))

    Although we differ on this one I guess we've given good arguments for both
    approaches so people can pick whatever they feel more comfortable with.

    Kind regards

    robert


    PS: Since I do Java for a living it's MT approach might have influenced me,
    there's simply no such thing as an application wide thread exclusive
    execution.
    Robert Klemme, Dec 27, 2005
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    3
    Views:
    1,480
  2. NaeiKinDus
    Replies:
    1
    Views:
    558
    Jack Klein
    Apr 14, 2007
  3. NaeiKinDus
    Replies:
    3
    Views:
    588
    James Kanze
    Apr 15, 2007
  4. Gunter Henriksen
    Replies:
    13
    Views:
    1,680
    Lawrence D'Oliveiro
    May 1, 2009
  5. sven
    Replies:
    2
    Views:
    1,909
    Roy Smith
    Dec 4, 2009
Loading...

Share This Page