Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 7: Synchronization Examples

Similar presentations


Presentation on theme: "Chapter 7: Synchronization Examples"— Presentation transcript:

1 Chapter 7: Synchronization Examples

2 Chapter 7: Synchronization Examples
Classic Problems of Synchronization Synchronization within the Kernel POSIX Synchronization Synchronization in Java Alternative Approaches

3 Classical Problems of Synchronization
Classical problems used to test newly-proposed synchronization schemes Bounded-Buffer Problem Readers and Writers Problem Dining-Philosophers Problem

4 Readers-Writers Problem
A data set is shared among a number of concurrent Threads Readers – only read the data set concurrently; they do not perform any updates Writers – can both read and write (update) concurrently Problem – allow multiple readers to read at the same time Only one single writer can access the shared data at the same time Several variations of how readers and writers are considered – all involve some form of priorities

5 Readers-Writers Problem Variations
First variation – no reader kept waiting unless writer has already access to shared database Second variation – once writer is ready, it performs the write ASAP Both may have starvation leading to even more variations In the first case, writers may starve; In the second case, readers may starve. Problem is solved on some systems by kernel providing reader-writer locks Shared Data Data set Semaphore rw_mutex = 1 // controls access to the database Semaphore mutex = 1 // controls access to read_count Integer read_count = 0 // number of active readers

6 Readers-Writers Problem (Cont.)
The structure of a writer process do { wait(rw_mutex); ... /* writing is performed */ ... signal(rw_mutex); } while (true); The structure of a reader process do { wait(mutex); read_count++; if (read_count == 1) wait(rw_mutex); signal(mutex); ... /* reading is performed */ ... wait(mutex); read count--; if (read_count == 0) signal(rw_mutex); } while (true);

7 Dining-Philosophers Problem
Philosophers spend their lives alternating thinking and eating Don’t interact with their neighbors, occasionally try to pick up 2 chopsticks (one at a time) to eat from bowl Need both to eat, then release both when done Grab chopstick – wait() operation on semaphore Releases chopstick – signal() operation on the semaphore In the case of 5 philosophers Shared data Bowl of rice (data set) Semaphore chopstick [5] initialized to 1

8 Dining-Philosophers Problem Algorithm
The structure of Philosopher i: do { wait (chopstick[i] ); // get left chopstick wait (chopStick[ (i + 1) % 5] ); // get right chopstick // eat signal (chopstick[i] ); // return left chopstick signal (chopstick[ (i + 1) % 5] ); // return right chopstick // think } while (TRUE); What is the problem with this algorithm?

9 Dining-Philosophers Problem (Cont.)
This solution guarantees that no neighbors eat simultaneously It can create a deadlock each philosopher grabs his/her left chopstick. All the elements of chopstick will be equal to 0. None can pick up the right chopstick and so delayed forever Remedies Allow at most four philosophers to sit at a time Allow to pick up chopsticks only if both are available (i.e. pick up in a critical section) Use an asymmetric solution (odd philosopher picks up first left chopstick and then right; even philosopher picks up right chopstick and then left chopstick). A deadlock-free solution does not necessarily eliminate the possibility of starvation.

10 Monitor Solution to Dining Philosophers
monitor DiningPhilosophers { enum { THINKING; HUNGRY, EATING) state [5] ; condition self [5]; void pickup (int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) self[i].wait; } void putdown (int i) { state[i] = THINKING; // test left and right neighbors test((i + 4) % 5); test((i + 1) % 5); void test (int i) { if ((state[(i + 4) % 5] != EATING) && (state[i] == HUNGRY) && (state[(i + 1) % 5] != EATING) ) { state[i] = EATING ; self[i].signal () ; } initialization_code() { for (int i = 0; i < 5; i++) state[i] = THINKING;

11 Solution to Dining Philosophers (Cont.)
Each philosopher i invokes the operations pickup() and putdown() in the following sequence: DiningPhilosophers.pickup(i); EAT DiningPhilosophers.putdown(i); No deadlock, but starvation is possible

12 A Monitor to Allocate Single Resource
monitor ResourceAllocator { boolean busy; condition x; void acquire(int time) { if (busy) x.wait(time); busy = TRUE; } void release() { busy = FALSE; x.signal(); initialization code() {

13 Monitor Solution to Readers/Writers
Shared database (for example, bank balances, or airline seats) Two classes of users: Readers – never modify database Writers – read and modify database Using a single lock on the database would be overly restrictive. Want: many readers at same time only one writer at same time

14 Monitor Solution to Readers/Writers
Constraints: Readers can access database when no writers (Condition okToRead) Writers can access database when no readers or writers (Condition okToWrite) Only one thread manipulates state variables at a time.

15 Monitor Solution to Readers/Writers
Basic structure of solution Reader wait until no writers access database check out – wake up waiting writer Writer wait until no readers or writers check out – wake up waiting readers or writer State variables: # of active readers – AR = 0 # of active writers – AW = 0 # of waiting readers – WR = 0 # of waiting writers – WW = 0 Condition okToRead = NIL Condition okToWrite = NIL Lock lock = FREE

16 Monitor Solution to Readers/Writers
Code: Reader() { // first check self into system lock.Acquire(); while ((AW + WW) > 0) { // check if safe to read if any writers, wait WR++; okToRead.Wait(&lock); WR--; } AR++; lock.Release(); Access DB // check self out of system AR--; if (AR == 0 && WW > 0) //if no other readers still active, wake up writer okToWrite.Signal(&lock);

17 Monitor Solution to Readers/Writers
Writer() { // symmetrical // check in lock.Acquire(); while ((AW + AR) > 0) { // check if safe to write if any readers or writers, wait WW++; okToWrite.Wait(&lock); WW--; } AW++; lock.Release(); Access DB // check out AW--; if (WW > 0) // give priority to other writers okToWrite.Signal(&lock); else if (WR > 0) okToRead.Broadcast(&lock);

18 Monitor Solution to Readers/Writers
Questions: Can readers starve? Why does checkRead need a while?

19 Modified Reader Reader() { // first check self into system
lock.Acquire(); while ((AW + WW) > 0) { // check if safe to read if any writers, wait WR++; ok.Wait(&lock); WR--; } AR++; lock.Release(); Access DB // check self out of system AR--; ok.broadcast(&lock);

20 Modified Writer Writer() { // symmetrical // check in lock.Acquire();
while ((AW + AR) > 0) { // check if safe to write if any readers or writers, wait WW++; ok.Wait(&lock); WW--; } AW++; lock.Release(); Access DB // check out AW--; if (WW > 0) or (WR > 0) ok.Broadcast(&lock);

21 Summary of Monitors lock while (need to wait) { wait(); } unlock
Monitors represent the logic of the program – wait if necessary, signal if change something so waiter might need to wake up. lock while (need to wait) { wait(); } unlock do something so no need to wait signal();

22 Language support for thread synchronization
The problem with requiring the programmer to specify both lock acquire and release statements is that he might forget to put a release everywhere it is needed. Languages like C This is not too bad in a language like C: just make sure you know all the code paths out of a critical section: Watch out for setjmp/longjmp! (Might happen in called procedure) int Rtn() { lock.acquire(); if (exception) { lock.release(); return errReturnCode; } return OK; Proc E calls longjmp Proc D Proc C lock.acquire Proc B calls setjmp Proc A

23 Language support for thread synchronization
Languages like C++ Languages that support exceptions – like C++ – are more problematic: Notice that an exception in DoFoo() will exit without releasing the lock void Rtn() { lock.acquire(); DoFoo(); lock.release(); } void DoFoo() { if (exception) throw errException;

24 Languages like C++ Rtn() needs to catch the exception, release the lock, and then re-throw the exception: void Rtn() { lock.acquire(); try { DoFoo(); } catch (…) { // C++ syntax for catching any exception. lock.release(); throw; // C++ syntax for re-throwing an exception.

25 Language support for thread synchronization
Java Java has explicit support for threads and thread synchronization. Bank account example: class Account { private int balance; // object constructor public Account (int initialBalance) { balance = initialBalance; } public synchronized int getBalance() { return balance; public synchronized void deposit(int amount) { balance += amount;

26 Java synchronized (object) { … }
Each Account object has an associated lock, which gets automatically acquired and released on entry and exit from each synchronized method. Java also has synchronized statements: Every Java object has an associated lock. Any Java object can be used to control access to a synchronized block of code. synchronized (object) { }

27 Java synchronized (object) { … DoFoo(); } void DoFoo() {
The synchronizing object’s lock is acquired on entry and released on exit, even if exit is by means of a thrown exception: synchronized (object) { DoFoo(); } void DoFoo() { throw errException;

28 Java How to wait in a synchronized method or code block:
void wait(long timeout); void wait(long timeout, int nanoseconds); void wait(); How to signal in a synchronized method or code block: void notify(); wakes up the longest waiting waiter void notifyAll(); like broadcast,wakes up all waiters

29 Java Notes: Only one condition variable per lock.
Condition variables can wait for a bounded length of time. This is useful for handling exception cases where something has failed. For example: One reason why all Java Virtual Machines are not equivalent: Different thread scheduling policies. The language specification does not stipulate whether preemptive scheduling is required or what granularity of time slice should be used if preemptive scheduling is provided. t1 = time.now(); while (!ATMRequest()) { wait(LONG_TIME); t2 = time.now(); if (t2 – t1 > LONG_TIME) CheckIfMachineBroken(); }

30 Synchronization Examples
Solaris Windows Linux Pthreads

31 Solaris Synchronization
Implements a variety of locks to support multitasking, multithreading (including real-time threads), and multiprocessing Uses adaptive mutexes for efficiency when protecting data from short code segments Starts as a standard semaphore spin-lock If lock held, and by a thread running on another CPU, spins If lock held by non-run-state thread, block and sleep waiting for signal of lock being released Uses condition variables Uses readers-writers locks when longer sections of code need access to data Uses turnstiles to order the list of threads waiting to acquire either an adaptive mutex or reader-writer lock Turnstiles are per-lock-holding-thread, not per-object Priority-inheritance per-turnstile gives the running thread the highest of the priorities of the threads in its turnstile

32 Windows Synchronization
Uses interrupt masks to protect access to global resources on uniprocessor systems Uses spinlocks on multiprocessor systems Spinlocking-thread will never be preempted Also provides dispatcher objects user-land which may act mutexes, semaphores, events, and timers Events An event acts much like a condition variable Timers notify one or more thread when time expired Dispatcher objects either signaled-state (object available) or non-signaled state (thread will block)

33 Linux Synchronization
Prior to kernel Version 2.6, disables interrupts to implement short critical sections Version 2.6 and later, fully preemptive Linux provides: Semaphores atomic integers spinlocks reader-writer versions of both On single-cpu system, spinlocks replaced by enabling and disabling kernel preemption

34 Pthreads Synchronization
Pthreads API is OS-independent It provides: mutex locks condition variable Non-portable extensions include: read-write locks spinlocks

35 Alternative Approaches
Transactional Memory OpenMP Functional Programming Languages

36 Transactional Memory { /* read/write memory */ }
A memory transaction is a sequence of read-write operations to memory that are performed atomically. void update() { /* read/write memory */ }

37 OpenMP { #pragma omp critical count += value }
OpenMP is a set of compiler directives and API that support parallel progamming. void update(int value) { #pragma omp critical count += value } The code contained within the #pragma omp critical directive is treated as a critical section and performed atomically.

38 Functional Programming Languages
Functional programming languages offer a different paradigm than procedural languages in that they do not maintain state. Variables are treated as immutable and cannot change state once they have been assigned a value. There is increasing interest in functional languages such as Erlang and Scala for their approach in handling data races.

39 End of Chapter 7


Download ppt "Chapter 7: Synchronization Examples"

Similar presentations


Ads by Google