Semaphores considered Edsger’ s perspective harmful “During system conception it transpired that we used the semaphores in Semaphores are “low-level” primitives. Small two completely different ways. The difference is so marked that, looking errors back, one wonders whether it was really fair to present the two ways as can introduce incorrect executions or grind the uses of the very same primitives. On the one hand, we have the program to a halt semaphores used for mutual exclusion, on the other hand, the private very difficult to debug semaphores.” The structure of the ’THE’-Multiprogramming System” Semaphores conflate two distinct uses Communications of the ACM v. 11 n. 5 May 1968. mutex condition synchronization (e.g., bounded buffer) 102 101 How did Monitors Enter Monitors come about? Collect shared data into an object/module First introduced as an OO programming language construct Define methods for accessing shared data synchronization object + methods Separate the concerns of mutual exclusion and condition synchronization calling a method defined in the monitor automatically acquires the lock They are comprised of Mesa, Java (synchronized methods) one lock, and A programming convention zero or more condition variables for managing can be defined in any language concurrent access to shared data 103 104
How do I wait for thee? Condition Variables Let me count the ways… An abstraction for conditional synchronization At the entry of the monitor associated with a monitor threads can queue on the mutex that protects the Enable threads to wait inside a critical monitor, waiting for the thread that is currently in section by releasing the monitor lock the monitor to exit (or to release the lock by starting to wait on a condition variable) A misnomer On a condition variable can neither be read nor set to a value threads can queue waiting on the associated think of them as a label associated with a condition condition on a resource and a queue thread can wait in the queue (inside the CS) until t hey are notified that condition holds 105 106 Condition Variables: Resource Variables Operations Condition variables (unlike semaphores) are stateless Three operations on condition variable x Each condition variable should be associated with a x.wait(lock) resource variable (RV) tracking the state of that Atomically: Release lock and go to sleep resource sleep by waiting on the queue associated with x It is your job to maintain the RV! Check its RV before calling wait() on a condition variable x.notify (historically called x.signal()) to ensure the resource is truly unavailable wake up a waiter if any; otherwise no-op wake up by moving waiter to the ready queue Once the resource is available, claim it (subtract the amount you are using!) x.notifyall (historically called x.broadcast()) Before notifying you are releasing a resource, indicate it Only called while holding a lock has become available by increasing the corresponding RV 107 108
Notify() semantics: Notify() Semantics Mesa vs. Hoare Mesa (or Brinch Hansen) semantics: Which thread executed once notify() is signaled thread is moved to ready list, but not called on CV? guaranteed to run right away if no thread is waiting on CV , notifier continues Hoare semantics: if one or more thread waiting on CV: signaling thread is suspended and, atomically, ownership of the lock is passed to one of the at least two ready threads: notifier and thread(s) waiting threads, whose execution is immediately that are moved from the queue of the CV to the resumed. ready queue notifying thread is resumed if former waiter exits only one can run… crucial section, or if it waits again …but which one? 109 110 What are the notify() vs notifyall() implications? (signal() vs. broadcast()) Mesa/Brinch Hansen Hoare It is always safe to use notifyall() instead of notify() signal() and broadcast() are hints Signaling is atomic with the only performance is affected resumption of waiting thread adding them affects performance, never safety shared state cannot change notify() is preferable when before waiting thread is resumed Shared state must be checked in a at most one waiting thread can make progress (e.g., with loop (could have changed! (tricky Shared state can be checked using mutual exclusion) tricky…)) an if statement any thread waiting on the condition variable can make robust to spurious wakeups Makes it easier to prove liveness progress Simple implementation Tricky to implement notifyall() is preferable when Used in most systems Does not support broadcast/notifyAll multiple waiting thread may be able to make progress Sponsored by a Turing Award Used in most books (but not yours!) Butler Lampson some waiting threads can make progress, others can’ t Sponsored by a Turing Award e.g., if a single CV is used for multiple predicates Tony Hoare 111
Condition Variables vs Which is Mesa? Semaphores Which is Hoare? wait() vs P() P() blocks threads only if value = 0 wait() always block and gives up monitor lock notify() vs V() V is stateful - if no thread is waiting, V() ensure future thread does not wait on P() if no waiting thread, notify() is a no op condition variables are stateless Code that uses monitors is easier to read Conditions for which threads are waiting are explicit 113 114 Producer-Consumer Producer-Consumer with Bounded Buffer with Bounded Buffer 0 N-1 0 N-1 in out in out / / remove item from buffer Shared: Monitor Producer Consumer { / / remove item from buffer / / add item to buffer int consume() { int buf[N]; char buf[N]; / / add item to buffer int consume() { lock.Acquire(); void produce(int item) { int in := 0, out := 0; Lock lock; void produce(int item) { full.P(); lock.Acquire() while (n == 0) Semaphore mutex_in(1), int n := 0, in := 0, out := 0; empty.P(); mutex_out.P(); wait(nonEmpty); while (n == N) mutex_out(1); Condition notEmpty, notFull; mutex_in.P(); int item := buf[out%N]; wait(notFull); int item := buf[out%N]; Semaphore empty(N), full(0); buf[in%N] := item; out := out+1; buf[in%N] := item; out := out+1; in := in+1; mutex_out.V(); n := n-1 in := in+1; mutex_in.V(); Semaphores Monitor notify(notFull); empty.V(); n := n+1; full.V(); return(item); notify(notEmpty); lock.Release(); } } return(item); lock.Release() } } 115 115 116 116
Kid and Cook Threads Kid and Cook Threads Monitor BurgerKing { Monitor BurgerKing { Lock mlock; Lock mlock; 0 0 int numburgers = 0; int numburgers = 0; condition burgerReady; condition burgerReady; kid_main() { cook_main() { kid_main() { cook_main() { dig_in_mud(); wake(); dig_in_mud(); wake(); void kid_eat() { void kid_eat() { BK.kid_eat(); shower(); BK.kid_eat(); shower(); mlock.acquire() mlock.acquire() bathe(); drive_to_work(); bathe(); drive_to_work(); while (numburgers==0) while (numburgers==0) draw_on_walls(); while(not_5pm) draw_on_walls(); while(not_5pm) burgerReady.wait() burgerReady.wait() BK.kid_eat(); BK.makeburger(); BK.kid_eat(); BK.makeburger(); numburgers -= 1 numburgers -= 1 facetime_Karthik(); drive_to_home(); facetime_Karthik(); drive_to_home(); mlock.release() mlock.release() facetime_oma(); watch_got(); facetime_oma(); watch_got(); } } sleep(); sleep(); BK.kid_eat(); BK.kid_eat(); void makeburger() { void makeburger() { } } } } mlock.acquire() mlock.acquire() ++numburger; ++numburger; Ready Ready burgerReady.signal(); burgerReady.signal(); inside Monitor inside Monitor mlock.release() mlock.release() } } Running Running Ready Ready } } 117 118 Kid and Cook Threads Kid and Cook Threads Monitor BurgerKing { Monitor BurgerKing { Lock mlock; Lock mlock; 0 0 int numburgers = 0; int numburgers = 0; condition burgerReady; condition burgerReady; kid_main() { cook_main() { kid_main() { cook_main() { wake(); wake(); dig_in_mud(); dig_in_mud(); void kid_eat() { void kid_eat() { shower(); shower(); BK.kid_eat(); BK.kid_eat(); mlock.acquire() mlock.acquire() bathe(); drive_to_work(); bathe(); drive_to_work(); while (numburgers==0) while (numburgers==0) draw_on_walls(); while(not_5pm) draw_on_walls(); while(not_5pm) burgerReady.wait() burgerReady.wait() BK.kid_eat(); BK.makeburger(); BK.kid_eat(); BK.makeburger(); numburgers -= 1 numburgers -= 1 facetime_Karthik(); drive_to_home(); facetime_Karthik(); drive_to_home(); mlock.release() mlock.release() facetime_oma(); watch_got(); facetime_oma(); watch_got(); } } BK.kid_eat(); sleep(); BK.kid_eat(); sleep(); void makeburger() { void makeburger() { } } } } mlock.acquire() mlock.acquire() ++numburger; ++numburger; Ready Ready burgerReady.signal(); burgerReady.signal(); inside Monitor inside Monitor mlock.release() mlock.release() } } Ready Running Ready Running } } girl swapped in girl executes 119 120
Recommend
More recommend