Synchronization Now that you have seen locks, is that all there is? No, but what is the “ right ” way to build a parallel program. Condition Synchronization Ø People are still trying to figure that out. Compromises: Ø between making it easy to modify shared variables AND Ø restricting when you can modify shared variables. Ø between really flexible primitives AND Ø simple primitives that are easy to reason about. 1 2 Beyond Locks Beyond Locks Locks ensure mutual exclusion Synchronizing on a condition. Bounded Buffer problem – producer puts things in a Ø When you start working on a synchronization problem, first define the mutual exclusion constraints, then ask “ when does fixed sized buffer, consumer takes them out. a thread wait ” , and create a separate synchronization Ø Synchronizing on a condition. variable representing each constraint. Class BoundedBuffer{ Bounded Buffer problem – producer puts things in a … void* buffer[]; fixed sized buffer, consumer takes them out. What is wrong Lock lock; with this? Ø What are the constraints for bounded buffer? int count = 0; Ø 1) only one thread can manipulate buffer queue at a time } ( mutual exclusion) BoundedBuffer::Remove(c){ BoundedBuffer::Deposit(c){ lock à acquire(); lock à acquire(); Ø 2) consumer must wait for producer to fill buffers if none full while (count == 0); // spin while (count == n); //spin ( scheduling constraint) Remove c from buffer; Add c to the buffer; Ø 3) producer must wait for consumer to empty buffers if all full count--; count++; ( scheduling constraint) lock à release(); lock à release(); } } 3 4 Beyond Locks Beyond Locks Class BoundedBuffer{ Class BoundedBuffer{ … … void* buffer[]; void* buffer[]; What is wrong What is wrong Lock lock; Lock lock; int count = 0; with this? int count = 0; with this? } } BoundedBuffer::Remove(c){ BoundedBuffer::Remove(c){ BoundedBuffer::Deposit(c){ BoundedBuffer::Deposit(c){ while (count == 0); // spin if (count == 0) sleep(); while (count == n); //spin if (count == n) sleep(); lock à acquire(); lock->acquire(); lock à acquire(); lock->acquire(); Remove c from buffer; Remove c from buffer; Add c to the buffer; Add c to the buffer; count--; count--; count++; count++; lock à release(); lock->release(); lock à release(); lock->release(); } if(count==n-1) wakeup(deposit); } if(count == 1) wakeup(remove); } } 5 6
Beyond Locks Beyond Locks Class BoundedBuffer{ … What is wrong void* buffer[]; with this? Lock lock; Class BoundedBuffer{ int count = 0; } … void* buffer[]; What is wrong Lock lock; BoundedBuffer::Remove(c){ with this? int count = 0; BoundedBuffer::Deposit(c){ while(1) { } while(1) { lock à acquire(); lock à acquire(); if (count == 0) { BoundedBuffer::Remove(c){ BoundedBuffer::Deposit(c){ if(count == n) { lock->release(); lock à acquire(); lock à acquire(); lock->release(); continue; if (count == 0) sleep(); continue;} if (count == n) sleep(); } Remove c from buffer; Add c to the buffer; Add c to the buffer; Remove c from buffer; count--; count++; count++; count--; if(count==n-1) wakeup(deposit); if(count == 1) wakeup(remove); lock à release(); lock à release(); lock à release(); lock à release(); break; break; } } }} }} 7 8 Introducing Condition Variables Condition Variables: Operations Correctness requirements for bounded buffer producer- Three operations Wait() usually specified a lock consumer problem Ø Wait() to be released as a parameter Ø Only one thread manipulates the buffer at any time (mutual ❖ Release lock exclusion) ❖ Go to sleep Ø Consumer must wait for producer when the buffer is empty ❖ Reacquire lock upon return (scheduling/synchronization constraint) ❖ Java Condition interface await() and awaitUninterruptably() Ø Producer must wait for the consumer when the buffer is full Ø Notify() (historically called Signal()) (scheduling/synchronization constraint) ❖ Wake up a waiter, if any ❖ Condition interface signal() Ø NotifyAll() (historically called Broadcast()) Solution: condition variables ❖ Wake up all the waiters Ø An abstraction that supports conditional synchronization ❖ Condition interface signalAll() Ø Condition variables are associated with a monitor lock Ø Enable threads to wait inside a critical section by releasing the Implementation monitor lock. Ø Requires a per-condition variable queue to be maintained Ø Threads waiting for the condition wait for a notify() 9 10 Implementing Wait() and Notify() Using Condition Variables: An Example Condition::Notify(lock){ schedLock->acquire(); if (lock->numWaiting > 0) { Coke machine as a shared buffer Move a TCB from waiting queue to ready queue; lock->numWaiting--; Two types of users } Ø Producer: Restocks the coke machine schedLock->release(); Ø Consumer: Removes coke from the machine } Requirements Condition::Wait(lock){ Why do we need Ø Only a single person can access the machine at any time schedLock->acquire(); schedLock? Ø If the machine is out of coke, wait until coke is restocked lock->numWaiting++; Ø If machine is full, wait for consumers to drink coke prior to restocking lock à release(); Put TCB on the waiting queue for the CV; How will we implement this? schedLock->release() switch(); Ø What is the class definition? lock à acquire(); Ø How many lock and condition variables do we need? } 11 12
Coke Machine Example Word to the wise… Class CokeMachine{ Always wait and notify condition variables with the … mutex held. Storge for cokes (buffer) Lock lock; Period. int count = 0; Condition notFull, notEmpty; } Ø Fine print: There are cases where notification outside of a lock can be safe, but the code tends to be fragile, error- CokeMachine::Deposit(){ CokeMachine::Remove(){ prone, and easy for another developer to break. lock à acquire(); lock à acquire(); Ø In many cases you can lose notifications and hang (liveness) while (count == n) { while (count == 0) { notFull.wait(&lock); } notEmpty.wait(&lock); } Ø Moreover there is no clear advantage to breaking this Add coke to the machine; Remove coke from to the machine; convention. So just don’t do it. count++; count--; notEmpty.notify(); notFull.notify(); lock à release(); lock à release(); } } 13 14 Summary Java syntax for condition variables Non-deterministic order of thread execution è concurrency Condition variables created from locks problems import java.util.concurrent.locks.ReentrantLock; Ø Multiprocessing public static final aLock = new ReentrantLock(); ❖ A system may contain multiple processors è cooperating threads/ processes can execute simultaneously public static ok = aLock.newCondition(); Ø Multi-programming public static int count; ❖ Thread/process execution can be interleaved because of time-slicing aLock.lock(); try { Goal: Ensure that your concurrent program works under ALL possible interleaving while(count < 16){ok.awaitUninterruptably()} } finally { Define synchronization constructs and programming style for aLock.unlock(); developing concurrent programs } ❖ Locks à provide mutual exclusion return 0; ❖ Condition variables à provide conditional synchronization 15 16
Recommend
More recommend