1 / 30

Advanced Synchronization Techniques Tutorial

Learn advanced synchronization techniques beyond semaphores to manage multi-threaded resource access efficiently. Explore alternatives like MultiSemaphore, Events, Monitors, and tackle common synchronization problems.

epps
Download Presentation

Advanced Synchronization Techniques Tutorial

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Tutorial 5 Even More Synchronization! presented by: Antonio Maiorano Paul Di Marco

  2. Quick Recap • When multiple threads share resources, we need a way to synchronize access to these resources • Recall the coffee example with pourer and drinker • Solution: Use semaphores!

  3. Alternatives to Semaphores • Semaphores are great, but they can be difficult to use when solving certain problems • We will look at alternatives to semaphores • Note: These alternatives are more convenient, though semaphores can still be used to solve these problems

  4. Problem 1 • 2 drummers (threads) each with his/her own drum set • Only 2 drumsticks: drumStick1 and drumStick2 (shared resources) • Both drummers want to play, and to play, a drummer needs two drumsticks

  5. Drummer 1: drumStick1.Wait(); drumStick2.Wait(); PlayDrums(); drumStick2.Signal(); drumStick1.Signal(); Drummer 2: drumStick1.Wait(); drumStick2.Wait(); PlayDrums(); drumStick2.Signal(); drumStick1.Signal(); One solution… Semaphore drumStick1 = new Semaphore(1); Semaphore drumStick2 = new Semaphore(2); This works, but…

  6. Drummer 1: drumStick1.Wait(); drumStick2.Wait(); PlayDrums(); drumStick2.Signal(); drumStick1.Signal(); Drummer 2: drumStick2.Wait(); drumStick1.Wait(); PlayDrums(); drumStick1.Signal(); drumStick2.Signal(); But what if… • Potential deadlock! • Problem: can’t rely on programmers to obtain semaphores in the same order

  7. Alternative: AND Synchronization • Two functions: • WaitMulti(S1, S2, …,Sn) : Blocks calling thread until all semaphores are available • SignalMulti(S1, S2, …, Sn) : Releases all semaphores • Easy to implement • See Java code: MultiSemTest.java

  8. Drummer 1: drumSticks.WaitMulti(); PlayDrums(); drumSticks.SignalMulti(); Drummer 2: drumSticks.WaitMulti(); PlayDrums(); drumSticks.SignalMulti(); Drummers… one more time! Semaphore drumStick1 = new Semaphore(1); Semaphore drumStick2 = new Semaphore(2); MultiSemaphore drumSticks; drumSticks = new MultiSemaphore(drumStick1, drumStick2); Problem solved!

  9. Problem 2 • Global keyboard state object : Know when keys are pressed • Threads: ask keyboard state object when a key has been pressed • Threads may start at any time

  10. Operating System thread: ... if (keyPressed = ‘A’) keyA.Signal(); ... User thread: ... // Wait for ‘A’ key to be // pressed before continuing keyA.Wait(); ... One solution… Semaphore keyA = new Semaphore(0); // Initially not pressed This works sometimes…

  11. What’s the problem? • Only one thread will react upon key press • If a thread starts after the key has been pressed, the Wait() call will return immediately • The semaphore is passive: it does not take timing into account • We need an active semaphore…

  12. Alternative: Events • Events are active semaphores with its functions defined as: • Wait() : Block until event occurs; calling thread is added to the event’s waiting queue. • Signal() : Unblock all threads on event’s waiting queue; remove them from the queue. • See Java code: EventTest.java

  13. One more time… Only diff! Event keyA = new Semaphore(0); // Initially not pressed Operating System thread: ... if (keyPressed = ‘A’) keyA.Signal(); ... User thread: ... // Wait for ‘A’ key to be // pressed before continuing keyA.Wait(); ...

  14. Monitors • A monitor is an Abstract Data Type – like a class • The key importance of a monitor is that only one function can be accessed at a time • An important property of ADTs for use with monitors is separation of interface and implementation

  15. Interface / Implementation Have a public interface to the class while maintaining the implementation private: • Prevents use of internal structures • Allows the implementation to change while keeping the same interface • Allows for control of how the object is used • Enforced interface allows for proving properties about its behaviour

  16. Monitor Analogy • A monitor can be thought of as a class where each function is a critical section • The monitor thus would have a mutex as a private data member, and • Each function would thus call mutex.Wait() before executing, and mutex.Signal() before exiting.

  17. In Java… • A monitor can be implemented as we just described, or • As a class where all of the methods are “synchronized”, where Java does the work for you

  18. Simple Monitor monitor SharedBalance { private int balance; public SharedBalance(int amt) {balance = amt;} public credit(int amt) {balance += amt;} public debit(int amt) {balance –= amt;} }

  19. Readers – Writers Problem • We have many readers and many writers • Readers want to read a resource • Writers want to write to a resource • Rules: • Multiple readers are allowed • One writer can write at a time • No readers can read when a writer is writing

  20. Building Our Solution • Before the reader or writer reads from the resource, it calls the its “start” method • The “start” methods ensure that the “coast is clear”; that it is fine to continue to read from/write to the resource • The “finish” methods allow the next thread to read or write

  21. Our Monitor • We want to solve the problem with a monitor, so it will need to keep track of: • int numReaders = 0: number of readers currently reading • int numWriters = 0: number of writers currently writing / waiting to write • boolean busy = false: is a writer busy?

  22. reader() { while (true){ // … mon.startRead(); // read the resource mon.finishRead(); // … } } writer() { while (true) { // … mon.startWrite(); // write the resource mon.finishWrite(); // … } } The Readers and Writers

  23. // reader methods startRead() { while (numWriters != 0); ++numReaders; } finishRead() { --numReaders; } // writer methods startWrite() { ++numWriters; while (busy || numReaders > 0); busy = true; } finishWrite() { --numWriters; busy = false; } Our Solution

  24. The Problem With Our Solution • While a reader/writer is executing it’s “start” in the monitor, no other processes are allowed in!!! • We have to look further for our solution… • Condition variables are used when: • a thread running in a monitor • encounters a condition that is not satisfied, • which can only be satisfied by another thread

  25. Condition Variables This is exactly what we need! • Condition Variables are an extension of Events to the concept of monitors • It temporarily blocks current thread, • Handing over “ownership” of the monitor to another thread – to do some work that will make the condition true • The original thread regains “ownership” of monitor later on…

  26. Regaining the Monitor What happens on a call to signal() depends on the semantics used • Hoare’s semantics: • Signaling thread (S) is immediately interrupted and monitor is given back to a blocked thread (B) • Thread S resumes (in the monitor) when B finishes using the monitor

  27. And Another Way… • Hansen’s sematics are not immediate: • Signal is “saved” • A blocked thread will wake up only when the signaling thread finishes with the monitor * The blocked thread must always verify the condition again upon waking up, since some time has elapsed since the signal() was called • This results in less context switches, increasing system performance

  28. How Do We Use Them? • Condition variables have three methods: • wait(): block on a variable, give up monitor • signal(): wake up a thread blocked on this • queue(): ask if are there any threads blocked on this variable - to be used as part of the condition • In Java, they correspond to the usage, within “synchronized” objects, of wait() and signal() – although there is no queue()

  29. Return to Readers – Writers • Now that we know about condition variables, we can solve this problem easily • We now have the following variables: • int numReaders = 0 (same as before) • boolean busy = false (same as before) • Condition Variables okToRd, okToWr • Solution will use Hoare’s semantics

  30. // reader methods startRead() { while (busy || okToWr.queue()) okToRead.wait(); ++numReaders; } finishRead() { --numReaders; if (numReaders == 0) okToWr.signal() } // writer methods startWrite() { ++numWriters; while (numReaders != 0 || busy) okToWr.wait(); busy = true; } finishWrite() { busy = false; if (okToWr.queue()) okToWr.signal(); else okToRead.signal(); } The Final Solution

More Related