1 / 76

Process/Thread Synchronization

Process/Thread Synchronization. Modified from Chapter 6 for 170 with Nachos threads, Tao Yang, 2012. Topics. The Critical-Section Problem & Solutions. Locks Semaphores Condition variables (monitors). Implementation Software solutions: Peterson’s Solution Synchronization Hardware

thaag
Download Presentation

Process/Thread Synchronization

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. Process/Thread Synchronization Modified from Chapter 6 for 170 with Nachos threads, Tao Yang, 2012

  2. Topics • The Critical-Section Problem & Solutions. • Locks • Semaphores • Condition variables (monitors). • Implementation • Software solutions: Peterson’s Solution • Synchronization Hardware • Synchronization Examples

  3. Objectives • To introduce the critical-section problem, whose solutions can be used to ensure the consistency of shared data • To present both software and hardware solutions of the critical-section problem • To introduce the concept of an atomic transaction and describe mechanisms to ensure atomicity

  4. A. Frank - P. Weisberg Multiple Producers and Consumers

  5. Background • Maintain countthat keeps track of the number of items to consume in the buffer.. • Concurrent access to shared data may result in data inconsistency

  6. Producer Code while (true) { while (count == BUFFER_SIZE) ; // do nothing buffer [in] = nextProduced; in = (in + 1) % BUFFER_SIZE; count++; }

  7. Consumer while (true) { while (count == 0) ; // do nothing nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; }

  8. Definitions • Race Conditions: two or more processes are reading and writing on shared data and the final result depends on who runs precisely when • Mutual exclusion: making sure that if one process is accessing a shared memory, the other will be excluded from doing the same thing • Critical region: the part of the program where shared variables are accessed

  9. Race Conditions: Example Count=5 Consumer thread Producer thread Count++ Count-- Is count still 5?

  10. Race Conditions: Example Count=5 Consumer thread Producer thread Count++: register1 = count register1 = register1 + 1 count = register1 Count—: register2 = count register2 = register2 - 1 count = register2 Is count still 5?

  11. Race Conditions: Example Count=5 Consumer thread Producer thread Count++: register1 = count register1 = register1 + 1 count = register1 Count—: register2 = count register2 = register2 - 1 count = register2 Is count still 5?

  12. Race Condition • “count = 5” initially: S0: producer execute register1 = count {register1 = 5}S1: producer execute register1 = register1 + 1 {register1 = 6} S2: consumer execute register2 = count {register2 = 5} S3: consumer execute register2 = register2 - 1 {register2 = 4} S4: producer execute count = register1 {count = 6 } S5: consumer execute count = register2 {count = 4}

  13. Property of Critical-Section Solutions • Mutual Exclusion – Only one can enter the critical section. • Progress- If some processes wish to enter their critical section and nobody is in the critical section, then one of them will enter in a limited time. • Bounded Waiting - If one process starts to wait for entering an critical section, there is a limit on the number of times other processes entering the section before this process enters.

  14. Critical-Section Management

  15. Solution to Critical-section Problem Locks Semaphore Conditional Variables acquire lock critical section release lock remainder section

  16. Semaphore • Semaphore S – integer variable • Can only be accessed /modified via two indivisible (atomic) operations • wait (S) { //also called P() while S <= 0 ; // wait S--; } • signal (S) { //also called V() S++; }

  17. Semaphore as General Synchronization Tool • Countingsemaphore – initial value representing how many threads can be in the critical section. • Binary semaphore (also known as mutex locks) – integer value ranged between 0 and 1. • Provides mutual exclusion Semaphore mutex; // initialized to 1 do { wait (mutex); // Critical Section signal (mutex); } while (TRUE);

  18. Nachos Threads • class Thread{ public: Thread (char *name); ~Thread(); void Fork( void (*Func)( int), int arg); void Yield(); void Finish(); }

  19. Nachos Threads: Methods supported • Thread(char *debugName). Create a thread. • Fork(VoidFunctionPtr func, int arg). A thread starts to execute a function. • Yield(). Suspend the calling thread and the system selects a new one for execution. • Sleep(). Suspend the current thread, change its state to BLOCKED, and remove it from the ready list • Finish()

  20. Sample Example of Nacho Threads main () { Thread *t1 = new Thread("forked thread1"); Thread *t2 = new Thread("forked thread2"); t1->Fork(SimpleThread, 1); t2->Fork(SimpleThread, 2); SimpleThread(3); } SimpleThread(int i) { printf(“Hello %d\n”, i); currentThread->Yield(); } Create 2 new threads. Start to fork and execute a function in each child thread. Parent also executes the same function Function executed by threads

  21. Nachos Lock Interface • Lock can be in one of two states: locked or unlocked • class Lock { public: Lock (char *name); //create a lock with unlocked state ~Lock(); void Acquire(); //Atomically waits until the lock is unlocked, and then sets the lock to be locked. void Release(); //Atomically changes the state to be unlocked. Only the thread who owns the lock can release. }

  22. How to use locks • Typically associate a lock with a piece of shared data for mutual execlusion. • When a thread needs to access, it first acquires the lock, and then accesses data. Once access completes, it releases the lock. • Example Lock *k= new Lock (“Lock”); k->Acquire(); Perform critical section operations. k->Release(); }

  23. Thread synchronization: Example The desired value for a is 2 at the end. Possible output: T0: a=1 T1: a=2 T0:a=2 T1: a=1 T0: a=1 T1:a=1 int a=0; void sum (int p){ a=a+1; printf(“T%d : a= %d\n”, p,a); } void main() { Thread *t=new Thread (“Child”); t->Fork(sum,1); sum(0); }

  24. Synchronization with Locks: Example int a=0; Lock *k; void sum (int p){ int c; k->Acquire(); a=a+1; c=a; k->Release() printf(“%d : a= %d\n”, p,c); } void main() { Thread *t=new Thread (“Child”); k=new Lock(“Lock”); t->Fork(sum,1); sum(0); }

  25. Semaphores in Nachos • class Semaphore{ public: Semaphore (char *name, int counter); // initial counter value ~Semaphore(); void P(); //Atomically waits until the counter is greater than 0 and then decreases the counter void V(); //Atomically increases the counter }

  26. Synchronization with Semaphore: Example int a=0; Semaphore *s; void sum (int p){ int c; s->P(); a=a+1; c=a; s->V(); printf(“%d : a= %d\n”, p,c); } void main() { Thread *t=new Thread (“Child”); s=new Semaphore(“S”,1); t->Fork(sum,1); sum(0); }

  27. Semaphore for consumer-producer problem with unbounded buffer • Semaphore can do more than mutual exclusion, and can synchronize a consumer/producer pipe • Semaphore *data =new Semaphore(“Data”, 0); • Indicate # of data items available for consuming • Consumer thread: while(1) { data-> P(); //wait until an item is available consume next item; } • Producer thread: while(1){ produce next item; data->V(); //notify an item is available }

  28. consumer-producer problem with bounded buffer • Two semaphores: • Semaphore *data =new Semaphore(“Data”, 0); • Indicate # of data items available for consuming • Semaphore *space =new Semaphore(“Space”, 10); • Indicate # of 10 data slots available. • Producer thread: while(1){ produce next item; data->V(); //notify item is available }

  29. consumer-producer problem with bounded buffer • Two semaphores: • Semaphore *data =new Semaphore(“Data”, 0); • Indicate # of data items available for consuming • Semaphore *space =new Semaphore(“Space”, 10); • Indicate # of 10 data slots available. • Producer thread: while(1){ space->P();//wait until space is available produce next item; data->V(); //notify data is available }

  30. consumer-producer problem with bounded buffer • Semaphore *data =new Semaphore(“Data”, 0); • Semaphore *space =new Semaphore(“Space”, 10); • Producer thread: while(1){ space->P();//wait until space is available produce next item; data->V(); //notify data is available } • Consumer thread: while(1){ data->P();//wait until data is available consume next item; }

  31. consumer-producer problem with bounded buffer • Semaphore *data =new Semaphore(“Data”, 0); • Semaphore *space =new Semaphore(“Space”, 10); • Producer thread: while(1){ space->P();//wait until space is available produce next item; data->V(); //notify data is available } • Consumer thread: while(1){ data->P();//wait until data is available consume next item; space->V();//notify space is available } }

  32. Solution to Critical-section Problem Locks Semaphore Conditional Variables

  33. Condition Variables • Improvement on busy waiting for acquiring a lock (spin locks) • A thread is looping forever in checking if a lock is available or not. • The process “spins”  Waste CPU resource. • Condition variables allow the waiting thread to be blocked, placed in a waiting queue, yielding CPU resource to somebody else. • Similar problem in semaphore implementation • wait (S) { //also called P() while (S <= 0) ; // no-op S--; }

  34. Nachos Condition Variables • Similar to Pthreads Condition Variables • class Condition { public: Condition(char *name); ~Condition(); void Wait(Lock *ConditionLock); //Atomically releases the lock and waits. When it is returned, the lock is reacquired again. void Signal(Lock *ConditionLock); //Wake up one waiting thread to run. The lock is not released. void Broadcast(Lock *ConditionLock); //Wake up all threads waiting on the condition. The lock is not released. }

  35. How to Use Condition Variables • Associate a lock (L) and condition variable (cv) with data items to be accessed. • An abstraction with lock/condition is known as monitor (Chapter 6.7). • Typical program flow: • Thread 1 L.Acquire(); When condition is not satisfied, cv.Wait(L); Critical Section; L.Release(); • Thread 2: L.Acquire(); When condition can satisfy, cv.Signal(L). L.Relase();

  36. Condition Variables for consumer-producer problem with unbounded buffer • int avail=0; // # of data items available for consumption • Lock *L; Condition *cv= new Condition (“condition”); • Consumer thread: L.Acquire(); while (avail <=0) cv.Wait(L); consume next item; avail = avail-1; L.Release(); • Producer thread: L.Acquire(); Produce next item; availl = avail+1; cv.Signal(L); //notify an item is available L.Release();

  37. When to use condition broadcast() • When waking up one thread to run is not sufficient. • Example: concurrent malloc()/free() for allocation and deallocation of objects with non-uniform sizes. • Lock *L; Condition *cv= new Condition (“condition”); • char *malloc(int s) { L.Acquire(); while( cannot allocate a chunk of size s) cv.Wait(L); allocate free space of size s; L.Release(); return the chunk allocated. }

  38. Use Broadcast() when space is freed • void free (char *m){ L.Acquire(); deallocate m; cv.Broadcast(L); //wake up all threads waiting for space L.Release(); }

  39. Running trace of malloc()/free() • Initially 10 bytes are free. • m() stands for malloc(). f() for free() Thread 2: m(5) – wait Resume m(5)-succ f(5) –broadcast Thread 3: m(5) – wait Resume m(5)-succ m(3) –wait Resume m(3)-succ Thread 1: m(10) – succ f(10) –broadcast m(7) – wait Resume m(7)-wait

  40. Implementation of Synchronization Primitives • Software solutions through shared memory data checking or message passing. • Hardware-assisted solutions • Properties (e.g. for implementing a lock) • Mutual exclusion: only one gets a lock. • Progress: when multiple threads try to acquire a lock, one can get it if nobody has it. • Bounded waiting: lock waiting time is limited.

  41. Software Solution for 2 processes/threads • The two processes share one variable: • intturn; // whose turn for the critical section • Process 0 • Process 1: • Mutual exclusion? Progress? Bounded waiting? while (turn != 0); critical section turn= 1; //give turn to P1 while (turn != 1); critical section turn= 0; //give turn to P0

  42. Mutual exclusion? • Prove by contraction. Assume both Processes 0 and 1 are in the critical section. • Process 0 • Process 1: • (turn==0) and (turn==1) is false while (turn != 0); critical section turn= 1; //give turn to P1 turn==0 while (turn != 1); critical section turn= 0; //give turn to P0 turn==1

  43. Peterson’s Solution • Two process software solution (Text book chapter 6.3) • Assume that the LOAD and STORE instructions are atomic; that is, cannot be interrupted. • The two processes share two variables: • intturn; • indicates whose turn it is to enter the critical section. • Boolean ready[2] • indicate if a process is ready to enter the critical section. ready[0]= true implies that process P0 is ready!

  44. Peterson’s Algorithm Process P0: ready[0] = TRUE; turn = 1; while (ready[1] && turn == 1); critical section ready[0] = FALSE; Enter critical section Exit critical section Process P1: ready[1] = TRUE; turn = 0; while (ready[0] && turn == 0); critical section ready[1] = FALSE; Mutual exclusion? Progress? Bounded waiting?

  45. Mutual Execution? Prove by contradition Process P0: ready[0] = TRUE; turn = 1; while (ready[1] && turn == 1); critical section ready[0] = FALSE; ready[0]=T and (Ready[1]=F or turn=0) Process P1: ready[1] = TRUE; turn = 0; while (ready[0] && turn == 0); critical section ready[1] = FALSE; ready[1]=T and (Ready[0]=F or turn=1) Both conditions are true, which is not possible

  46. Progress? Can both P0/P1 wait forever? Process P0: ready[0] = TRUE; turn = 1; while (ready[1] && turn == 1); critical section ready[0] = FALSE; (Ready[1]=T and turn=1) Process P1: ready[1] = TRUE; turn = 0; while (ready[0] && turn == 0); critical section ready[1] = FALSE; (Ready[0]=T and turn=0) Both conditions are true, which is not possible

  47. Progress? Can P0 wait forever after P1 leaves Process P0: ready[0] = TRUE; turn = 1; while (ready[1] && turn == 1); critical section ready[0] = FALSE; (ready[1]=T and turn=1) Process P1: ready[1] = TRUE; turn = 0; while (ready[0] && turn == 0); critical section ready[1] = FALSE; ready[1]=F Both conditions are true, which is not possible.

  48. Bounded waiting? Can P0 wait forever after P1 leaves Process P0: ready[0] = TRUE; turn = 1; while (ready[1] && turn == 1); critical section ready[0] = FALSE; (ready[1]=T and turn=1) Process P1: ready[1] = TRUE; turn = 0; while (ready[0] && turn == 0); critical section ready[1] = FALSE; ready[1]=F Both conditions are true, which is not possible. P0 gets in after at most one entry by P1

  49. Hardware Solution for Synchronization • Many systems provide hardware support for critical section code • Uniprocessors – could disable interrupts • Currently running code would execute without preemption • Project 1. • Generally too inefficient on multiprocessor systems • Operating systems using this not broadly scalable • Modern machines provide special atomic hardware instructions • Atomic = non-interruptable • Either test memory word and set value • Or swap contents of two memory words

  50. TestAndSet Instruction • Definition: Get the value of a shared variable and then set it to TRUE atomically. boolean TestAndSet (boolean *target) { boolean rv = *target; *target = TRUE; return rv: }

More Related