130 likes | 145 Views
This tutorial discusses the problem of synchronization in a producer-consumer scenario and presents the solution using semaphores. It also covers the use of semaphores in handling multiple consumers.
E N D
Tutorial 3 Sync or sink! presented by: Antonio Maiorano Paul Di Marco
What’s the problem? • Example • A cup of coffee • A “pourer” (producer) • A “drinker” (consumer) • Result: A mess! Pourer: while (true){ pour(); } Drinker(s): while (true){ drink(); }
So, what do we want? Simple protocol: • Pourer pours only when cup is empty • Drinker drinks only when cup is full • Pourer: • while (true){ • if (!full){ • pour(); • full = true; • } • } Drinker(s): while (true){ if (full) { drink(); full = false; } }
Ok, but… … what if we have two drinkers? • Both drinkers can drink at the same time! Drinker 1: while (true){ if (full) { drink(); full = false; } } Drinker 2: while (true){ if (full) { drink(); full = false; } } Need this to be atomic!
Fine! So how can we do this? Use Semaphores! • A semaphore is an object used to synchronize multiple threads. • Two functions: • Wait() : willing • Signal() : release the lock • Both functions are guaranteed to be atomic
Semaphore Example Clients arriving at a restaurant with only 10 seats. • Manager = Semaphore • Clients = Threads • 10 Seats = 10 Limited resources controlled using the semaphore.
Semaphore Java Code class Semaphore { private int value; Semaphore (int value1) { value = value1; } public synchronized void Wait () { while( value <= 0 ) { try { wait (); } catch (InterruptedException e) { }; } value--; } public synchronized void Signal () { ++value; notify (); } }
Multiple drinkers? No problem! • Using semaphores, we can make sure that only one drinker can ever drink at one time • Important: Wait() and Signal() are guaranteed to be atomic! Main: // Semaphore initialization Semaphore cup; cup = new Semaphore(1); Drinker i: while (true){ sem.Wait(); drink(); sem.Signal(); }
Programming Assignment 2 Overview of the Problems: • The run() of Acquire and Release are not critical sections, hence they could leave Block in an inconsistent internal state • Acquires are not to run before the Releases complete
To make matters worse… Not only: • are the run() functions not mutually exclusive – yields are inserted to force it to go wrong! • must the Acquires wait for the Releases to finish – the Acquires are put on the ready queue first! Ready Queue after all threads are created: main > A1 > A2 > R1 > R2
“yield()s at crucial junctures” • These explicityield()’s represent what could be a time-slice context-switch in a “real-world” environment ++Block.top; ReleaseBlock : yield (); (push) Block.stack[Block.top] = block ; block = Block.stack[Block.top]; AcquireBlock: yield (); (pop) --Block.top;
Semaphore Initial Values Note that the mutex and semaphores are initialized to different values: • The mutex is initialized to one to allow only one thread into the critical section at any time • The ‘prisem’ semaphores are initialized to zero to allow no Acquires to run (initially)
Finally - Assignment Solution!!! Yeah right! • “Use the mutual-exclusion semaphore to make ‘acquire’ and ‘release’ atomic” • “Use the two ‘prisem’ semaphores to enforce the ordering that all ReleaseBlock threads must complete before any AcquireBlock threads can begin.”