OSC___CH06___Process_Synchronization__Contd__
OSC___CH06___Process_Synchronization__Contd__
Monitors
[email protected]
School of Computer Science and Engineering,
Southeast University
Contents
1 Condition Variables
2 Semaphores
3 Monitors
Condition Variables
Implementing pthread join()
• Two problems:
1 void ∗ c h i l d ( void ∗arg ) {
2 p i n t f ( ” c h i l d \n” ) ;
3 // Problem #1: how t o i n d i c a t e we a r e done ?
4 r e t u r n NULL ;
5 }
6
7 i n t main ( i n t a r g c , c h a r ∗ a r g v [ ] ) {
8 p r i n t f ( ” p a r e n t : b e g i n\n” ) ;
9 pthread t c ;
10 p t h r e a d c r e a t e (&c , NULL , c h i l d , NULL) ;
11 // Problem #2: how t o w a i t f o r c h i l d ?
12 p r i n t f ( ” p a r e n t : end\n” ) ;
13 return 0;
14 }
Condition Variables
Implementing pthread join() (contd.)
• Correct, but
• Waste of CPU time — spin-waiting.
• Do we need a lock here? Not a critical section.
Condition Variables
Implementing pthread join() (contd.)
Condition Variables
Implementing pthread join() (contd.)
1 i n t done = 0 ;
2 pthread mutex m =
1 void t h r j o i n () {
PTHREAD MUTEX INITIALIZER ;
2 p t h r e a d m u t e x l o c k (&m) ;
3 pthread cond t c =
3 w h i l e ( done == 0 )
PTHREAD COND INITIALIZER ;
4 p t h r e a d c o n d w a i t (&c , &m) ;
4
5 p t h r e a d m u t e x u n l o c k (&m) ;
5 void t h r e x i t () {
6 }
6 p t h r e a d m u t e x l o c k (&m) ;
7
7 done = 1 ;
8 i n t main ( i n t a r g c , c h a r ∗ a r g v [ ] ) {
8 p t h r e a d c o n d s i g n a l (& c ) ;
9 p r i n t f ( ” p a r e n t : b e g i n\n” ) ;
9 p t h r e a d m u t e x u n l o c k (&m) ;
10 pthread t p ;
10 }
11 p t h r e a d c r e a t e (&p , NULL , c h i l d , NULL) ;
11
12 t h r j o i n () ;
12 void ∗ c h i l d ( void ∗arg ) {
13 p r i n t f ( ” p a r e n t : end\n” ) ;
13 p r i n t f ( ” c h i l d \n” ) ;
14 return 0;
14 t h r e x i t () ;
15 }
15 r e t u r n NULL ;
16 }
Condition Variables
Implementing pthread join() (contd.)
1 void t h r e x i t () { 1 void t h r e x i t () {
2 pthread m u t e x l o c k (&m) ; 2 done = 1 ;
3 pthread c o n d s i g n a l (& c ) ; 3 p t h r e a d c o n d s i g n a l (& c ) ;
4 pthread m u t e x u n l o c k (&m) ; 4 }
5 } 5
6 6
7 void t h r j o i n () { 7 void t h r j o i n () {
8 pthread m u t e x l o c k (&m) ; 8 i f ( done == 0 )
9 pthread c o n d w a i t (&c , &m) ; 9 p t h r e a d c o n d w a i t (& c ) ;
10 pthread m u t e x u n l o c k (&m) ; 10 }
11 } 11
12 12
13 /∗ b r o k e n c o d e #1 ∗/ 13 /∗ b r o k e n c o d e #2 ∗/
Condition Variables
The Producer-Consumer Problem
1 /∗ L e t ’ s b e g i n w i t h o n l y 1 p r o d u c e r , 1 consumer , b u f f e r s i z e = 1 ∗/
2
3 c o n d t cond ;
4 m u t e x t mutex
5 void ∗producer ( void ∗arg ) {
6 f o r ( i n t i = 0 ; i < l o o p s ; i ++) {
7 p t h r e a d m u t e x l o c k (&mutex ) ;
8 i f ( c o u n t == 1 )
9 p t h r e a d c o n d w a i t (&cond , &mutex ) ;
10 put ( ) ; // p r o d u c e
11 p t h r e a d c o n d s i g n a l (& cond ) ;
12 p t h r e a d m u t e x u n l o c k (&mutex ) ;
13 }
14 }
15 v o i d ∗c o n s u m e r ( v o i d ∗ a r g ) {
16 f o r ( i n t i = 0 ; i < l o o p s ; i ++) {
17 p t h r e a d m u t e x l o c k (&mutex ) ;
18 i f ( c o u n t == 0 )
19 p t h r e a d c o n d w a i t (&cond , &mutex ) ;
20 get () ; // consume
21 p t h r e a d c o n d s i g n a l (& cond ) ;
22 p t h r e a d m u t e x u n l o c k (&mutex ) ;
23 }
24 }
Condition Variables
The Producer-Consumer Problem (contd.)
Condition Variables
The Producer-Consumer Problem (contd.)
1 /∗ S t i l l b r o k e n ∗/
2
3 c o n d t cond ;
4 m u t e x t mutex
5 void ∗producer ( void ∗arg ) {
6 f o r ( i n t i = 0 ; i < l o o p s ; i ++) {
7 p t h r e a d m u t e x l o c k (&mutex ) ;
8 w h i l e ( c o u n t == 1 ) // u s e ” w h i l e ” i n s t e a d o f ” i f ”
9 p t h r e a d c o n d w a i t (&cond , &mutex ) ;
10 put ( ) ; // p r o d u c e
11 p t h r e a d c o n d s i g n a l (& cond ) ;
12 p t h r e a d m u t e x u n l o c k (&mutex ) ;
13 }
14 }
15 v o i d ∗c o n s u m e r ( v o i d ∗ a r g ) {
16 f o r ( i n t i = 0 ; i < l o o p s ; i ++) {
17 p t h r e a d m u t e x l o c k (&mutex ) ;
18 w h i l e ( c o u n t == 0 ) // u s e ” w h i l e ” i n s t e a d o f ” i f ”
19 p t h r e a d c o n d w a i t (&cond , &mutex ) ;
20 get () ; // consume
21 p t h r e a d c o n d s i g n a l (& cond ) ;
22 p t h r e a d m u t e x u n l o c k (&mutex ) ;
23 }
24 }
Condition Variables
The Producer-Consumer Problem (contd.)
Condition Variables
The Producer-Consumer Problem (contd.)
1 /∗ b u f f e r s i z e = 1 ∗/
2
3 c o n d t empty , f i l l ; // two c o n d i t i o n v a r i a b l e s
4 m u t e x t mutex
5 void ∗producer ( void ∗arg ) {
6 f o r ( i n t i = 0 ; i < l o o p s ; i ++) {
7 p t h r e a d m u t e x l o c k (&mutex ) ;
8 w h i l e ( c o u n t == 1 )
9 p t h r e a d c o n d w a i t (&empty , &mutex ) ;
10 put ( ) ; // p r o d u c e
11 p t h r e a d c o n d s i g n a l (& f i l l ) ;
12 p t h r e a d m u t e x u n l o c k (&mutex ) ;
13 }
14 }
15 v o i d ∗c o n s u m e r ( v o i d ∗ a r g ) {
16 f o r ( i n t i = 0 ; i < l o o p s ; i ++) {
17 p t h r e a d m u t e x l o c k (&mutex ) ;
18 w h i l e ( c o u n t == 0 )
19 p t h r e a d c o n d w a i t (& f i l l , &mutex ) ;
20 get () ; // consume
21 p t h r e a d c o n d s i g n a l (&empty ) ;
22 p t h r e a d m u t e x u n l o c k (&mutex ) ;
23 }
24 }
Condition Variables
The Producer-Consumer Problem (contd.)
1 /∗ d e a l i n g w i t h bounded b u f f e r i n s t e a d o f s i n g l e b u f f e r ∗/
2
3 c o n d t empty , f i l l ; // s t i l l two c o n d i t i o n v a r i a b l e s
4 m u t e x t mutex
5 void ∗producer ( void ∗arg ) {
6 f o r ( i n t i = 0 ; i < l o o p s ; i ++) {
7 p t h r e a d m u t e x l o c k (&mutex ) ;
8 w h i l e ( c o u n t == MAX) // remember : a l w a y s u s e ” w h i l e ”
9 p t h r e a d c o n d w a i t (&empty , &mutex ) ;
10 put ( ) ; // p r o d u c e
11 p t h r e a d c o n d s i g n a l (& f i l l ) ;
12 p t h r e a d m u t e x u n l o c k (&mutex ) ;
13 }
14 }
15 v o i d ∗c o n s u m e r ( v o i d ∗ a r g ) {
16 f o r ( i n t i = 0 ; i < l o o p s ; i ++) {
17 p t h r e a d m u t e x l o c k (&mutex ) ;
18 w h i l e ( c o u n t == 0 )
19 p t h r e a d c o n d w a i t (& f i l l , &mutex ) ;
20 get () ; // consume
21 p t h r e a d c o n d s i g n a l (&empty ) ;
22 p t h r e a d m u t e x u n l o c k (&mutex ) ;
23 }
24 }
Condition Variables
Covering Condition
Contents
1 Condition Variables
2 Semaphores
3 Monitors
Semaphores
• A semaphore is an object with an integer value that we can manipulate with two
routines: wait() and signal().
• In the POSIX standard, these routines are sem wait() and sem post().
• Historically, Dijkstra uses P() and V (). P() comes from “prolaag”, a
contraction of “probeer” (Dutch for “try”) and “verlaag” (“decrease”);
V () comes from the Dutch word “verhoog” which means “increase”.
1 // C o r r e c t ! 1 // I n c o r r e c t !
2 wait ( s ) { 2 wait ( s ) {
3 w h i l e ( s <= 0 ) ; // S p i n ! 3 s −−;
4 s −−; 4 w h i l e ( s < 0) ;
5 } 5 }
6 6
7 signal (s) { 7 signal (s) {
8 s ++; 8 s ++;
9 } 9 }
Semaphores
Semaphore Implementation
Semaphores
Why Semaphores
• Functionality
• A single primitive for all things related to synchronization
• Both locks and condition variables
• Correctness and Convenience
• Avoid errors — can signal() first then wait().
• Clean and organized — making it easy to demonstrate their correctness.
• Efficient.
Semaphores
Basic Synchronization Patterns
• Signaling
• Rendezvous
• Mutex
• Multiplex
• Barrier
• Reusable barrier
• Pairing
Semaphores
Signaling
1 /∗ Thread A ∗/ 1 /∗ Thread B ∗/
2 statement a ; 2 statement b ;
1 /∗ Thread A ∗/ 1 /∗ Thread B ∗/
2 statement a ; 2 wait ( s ) ;
3 signal (s) ; 3 statement b ;
Semaphores
Rendezvous
1 /∗ Thread A ∗/ 1 /∗ Thread B ∗/
2 s t a t e m e n t a1 ; 2 s t a t e m e n t b1 ;
3 s t a t e m e n t a2 ; 3 s t a t e m e n t b2 ;
1 /∗ Thread A ∗/ 1 /∗ Thread B ∗/
2 s t a t e m e n t a1 ; 2 s t a t e m e n t b1 ;
3 signal (a) ; 3 signal (b) ;
4 wait (b) ; 4 wait ( a) ;
5 s t a t e m e n t a2 ; 5 s t a t e m e n t b2 ;
Semaphores
Rendezvous (contd.)
1 /∗ Thread A ∗/ 1 /∗ Thread B ∗/
2 s t a t e m e n t a1 ; 2 s t a t e m e n t b1 ;
3 wait (b) ; 3 wait ( a) ;
4 signal (a) ; 4 signal (b) ;
5 s t a t e m e n t a2 ; 5 s t a t e m e n t b2 ;
Semaphores
Mutex
1 /∗ Thread A ∗/ 1 /∗ Thread B ∗/
2 count = count + 1; 2 count = count + 1;
Semaphores
Multiplex
• Generalize the previous solution so that it allows multiple threads to run in the
critical section at the same time, but it enforces an upper limit (MAX ) on the
number of concurrent threads.
1 wait ( multiplex ) ;
2 count = count + 1; // c r i t i c a l section
3 signal ( multiplex ) ;
Semaphores
Barrier
• Generalize the rendezvous solution. n threads should run the following code:
1 rendezvous ;
2 c r i t i c a l point ;
• How to ensure that no thread executes critical point until after all threads have
executed rendezvous?
1 rendezvous ;
2 w a i t ( mutex ) ;
3 count = count + 1;
1 /∗ i n i t i a l i z a t i o n ∗/
4 s i g n a l ( mutex ) ;
2 i n t count = 0;
5 i f ( c o u n t == n )
3 s e m a p h o r e mutex = 1 ;
6 signal ( barrier ) ;
4 semaphore b a r r i e r = 0 ;
7 wait ( b a r r i e r ) ;
8 signal ( barrier ) ;
9 c r i t i c a l point ;
Semaphores
Barrier (contd.)
1 /∗ Bad b a r r i e r s o l u t i o n ∗/
2 rendezvous ;
3 w a i t ( mutex ) ;
4 count = count + 1;
5 i f ( c o u n t == n )
6 signal ( barrier ) ;
7 wait ( b a r r i e r ) ;
8 signal ( barrier ) ;
9 s i g n a l ( mutex ) ;
10 c r i t i c a l point ;
Semaphores
Reusable Barrier (contd.)
1 s e m a p h o r e b a r r i e r 1 = 0 , b a r r i e r 2 = 0 , mutex = 1 ;
2
3 rendezvous ;
4 w a i t ( mutex ) ;
5 c o u n t += 1 ;
6 i f ( c o u n t == n )
7 for ( int i = 0 ; i < n ; i ++)
8 signal ( barrier1 ) ;
9 s i g n a l ( mutex ) ;
10 wait ( b a r r i e r 1 ) ;
11 c r i t i c a l point ;
12 w a i t ( mutex ) ;
13 c o u n t −= 1 ;
14 i f ( c o u n t == 0 )
15 for ( int i = 0 ; i < n ; i ++)
16 signal ( barrier2 ) ;
17 s i g n a l ( mutex ) ;
18 wait ( b a r r i e r 2 ) ;
Semaphores
Pairing
• Imagine that threads represent ballroom dancers and that two kinds of dancers,
leaders and followers, wait in two queues before entering the dance floor. When
a leader arrives, it checks to see if there is a follower waiting. If so, they can
both proceed. Otherwise it waits. Similarly, when a follower arrives, it checks for
a leader and either proceeds or waits, accordingly.
1 semaphore l e a d e r = 0 , f o l l o w e r = 0 ;
1 /∗ l e a d e r ∗/ 1 /∗ f o l l o w e r ∗/
2 signal ( leader ) ; 2 signal ( follower ) ;
3 wait ( f o l l o w e r ) ; 3 wait ( leader ) ;
4 dance ( ) ; 4 dance ( ) ;
Semaphores
Pairing (contd.)
1 /∗ i n i t i a l i z a t i o n ∗/
2 i n t n u m l = 0 , num f = 0 ;
3 s e m a p h o r e l e a d e r = 0 , f o l l o w e r = 0 , p a i r i n g = 0 , mutex = 1 ;
1 /∗ l e a d e r ∗/ 1 /∗ f o l l o w e r ∗/
2 w a i t ( mutex ) ; 2 w a i t ( mutex ) ;
3 i f ( num f > 0 ) { 3 i f ( num l > 0) {
4 num f −−; 4 n u m l −−;
5 signal ( leader ) ; 5 signal ( follower ) ;
6 } 6 }
7 else { 7 else {
8 n u m l ++; 8 num f ++;
9 s i g n a l ( mutex ) ; 9 s i g n a l ( mutex ) ;
10 wait ( f o l l o w e r ) ; 10 wait ( leader ) ;
11 } 11 }
12 dance ( ) ; 12 dance ( ) ;
13 wait ( p a i r i n g ) ; 13 signal ( pairing ) ;
14 s i g n a l ( mutex ) ; 14 // no s i g n a l ( mutex ) ;
Semaphore
The Producer-Consumer Problem
Semaphore
The Dining Philosophers
• There are five “philosophers” sitting around a table. Between each pair of
philosophers is a single chopstick (and thus, five total). The philosophers each
have times where they think, and don’t need any chopsticks, and times where
they eat. In order to eat, a philosopher needs two chopsticks, both the one on
their left and the one on their right. The basic loop of each philosopher is as
follows, assuming each has a unique identifier p ∈ [0, 4]:
1 while ( true ) {
2 think () ;
3 getchopsticks () ;
4 eat () ;
5 putchopsticks () ;
6 }
Semaphore
The Dining Philosophers (contd.)
1 /∗ a f a i l e d s o l u t i o n , why f a i l e d ? ∗/
2
3 int l e f t ( int p) { return p ; }
4 int r i g h t ( i n t p ) {r e t u r n ( p + 1) % 5 ; }
5
6 void putchopsticks () {
7 signal ( chopsticks [ l e f t (p) ]) ;
8 signal ( chopsticks [ right (p) ]) ;
9 }
10
11 void getchopsticks () {
12 wait ( chopsticks [ l e f t (p) ] ) ;
13 wait ( chopsticks [ r i g h t (p) ] ) ;
14 }
Semaphore
The Dining Philosophers (contd.)
1 /∗ a c o r r e c t s o l u t i o n ∗/
2
3 int l e f t ( int p) { return p ; }
4 int r i g h t ( i n t p ) {r e t u r n ( p + 1) % 5 ; }
5
6 void putchopsticks () {
7 signal ( chopsticks [ l e f t (p) ]) ;
8 signal ( chopsticks [ right (p) ]) ;
9 }
10
11 void getchopsticks () {
12 i f ( p == 4 ) {
13 wait ( chopsticks [ right (p) ]) ;
14 wait ( chopsticks [ l e f t (p) ]) ;
15 }
16 else {
17 wait ( chopsticks [ l e f t (p) ]) ;
18 wait ( chopsticks [ right (p) ]) ;
19 }
20 }
Semaphore
The Readers-Writers Problem
Semaphore
The First Readers-Writers Problem
1 semaphore w r i t e m u t e x = 1 ;
2 semaphore readcount mutex = 1 ;
3 int read count = 0;
1 void read () {
2 do {
3 wait ( readcount mutex ) ;
4 r e a d c o u n t ++;
1 void write () { 5 i f ( r e a d c o u n t == 1 )
2 do { 6 wait ( write mutex ) ;
3 wait ( write mutex ) ; 7 s i g n a l ( readcount mutex ) ;
4 /∗ w r i t i n g ∗/ 8 /∗ r e a d i n g ∗/
5 s i g n a l ( write mutex ) ; 9 wait ( readcount mutex ) ;
6 } 10 r e a d c o u n t −−;
7 while ( true ) ; 11 i f ( r e a d c o u n t == 0 )
8 } 12 s i g n a l ( write mutex ) ;
13 s i g n a l ( readcount mutex ) ;
14 }
15 while ( true ) ;
16 }
Semaphore
The No-starve Readers-Writers Problem
1 s e m a p h o r e r e a d c o u n t m u t e x= 1 ;
2 s e m a p h o r e w r i t e m u t e x =1;
3 int read count = 0;
4 s e m a p h o r e r e a d m u t e x =1;
1 void read () {
2 do {
3 wait ( readmutex ) ;
4 s i g n a l ( readmutex ) ;
1 void write () { 5 wait ( readcount mutex ) ;
2 do { 6 r e a d c o u n t ++;
3 wait ( readmutex ) ; 7 i f ( r e a d c o u n t == 1 )
4 wait ( writemutex ) ; 8 wait ( writemutex ) ;
5 /∗ w r i t i n g ∗/ 9 s i g n a l ( readcount mutex ) ;
6 s i g n a l ( writemutex ) ; 10 /∗ r e a d i n g ∗/
7 s i g n a l ( readmutex ) ; 11 wait ( readcount mutex ) ;
8 } 12 r e a d c o u n t −−;
9 while ( true ) ; 13 i f ( r e a d c o u n t == 0 )
10 } 14 s i g n a l ( writemutex ) ;
15 s i g n a l ( readcount mutex ) ;
16 }
17 while ( true ) ;
18 }
Semaphore
The Second Readers-Writers Problem
1 semaphore r e a d c o u n t m u t e x= 1 ;
2 semaphore w r i t e m u t e x =1;
3 int write count = read count = 0;
4 semaphore w r i t e c o u n t m u t e x= 1 ;
5 semaphore r e a d m u t e x =1;
Semaphores
Why and Why Not Semaphores
• Why semaphores?
• Avoid errors/Clean and organized/Efficient.
• Synchronization between Processes.
• How about performance? Better than you can imagine.
• Why not semaphores?
• Signaling all (broadcast) in CV.
• Priority inheritance in mutex locks.
Contents
1 Condition Variables
2 Semaphores
3 Monitors
Monitors
• Why monitors?
• As object-oriented programming was gaining ground, people started to
think about ways to merge synchronization into a more structured
programming environment.
• With a monitor class — the monitor guarantees that only one thread can be
active within the monitor at a time.
• A Java Monitor — add the keyword synchronized to the method or set of
methods that you wish to use as a monitor
1 public c l a s s SynchronizaedCounter {
2 private int c = 0;
3 public synchronized void increment () {
4 c ++;
5 }
6 p u b l i c s y n c h r o n i z e d void decrement () {
7 c −−;
8 }
9 public synchronized int value () {
10 return c ;
11 }
12 }
Monitors
Hoare Vs. Mesa
• Hoare Semantics:
• The signal() immediately wakes one waiting thread.
• Guess which one is more popular?
• Mesa semantics:
• The signal() move a single waiting thread to ready state.
Monitors
Hoare Vs. Mesa (contd.)
1 /∗ c o r r e c t w i t h Hoare s e m a n t i c , b u t i n c o r r e c t w i t h Mesa s e m a n t i c ∗/
2 monitor c l a s s BoundedBuffer {
3 p r i v a t e : i n t b u f f e r [MAX ] ;
4 i n t f i l l , use , f u l l E n t i r e s = 0 ;
5 c o n d t empty , f u l l ;
6 p u b l i c : void produce ( i n t element ) {
7 i f ( f u l l E n t i r e s == MAX)
8 // c o r r e c t w i t h Mesa c h a n g i n g ” i f ” t o ” w h i l e ”
9 w a i t (&empty ) ;
10 b u f f e r [ f i l l ] = element ;
11 f i l l = ( f i l l + 1 ) % MAX;
12 f u l l E n t r i e s ++;
13 s i g n a l (& f u l l ) ;
14 }
15 i n t consume ( ) {
16 i f ( f u l l E n t r i e s == 0 )
17 // c o r r e c t w i t h Mesa c h a n g i n g ” i f ” t o ” w h i l e ”
18 w a i t (& f u l l ) ;
19 i n t tmp = b u f f e r [ u s e ] ;
20 u s e = ( u s e + 1 ) % MAX;
21 f u l l E n t r i e s −−;
22 s i g n a l (&empty ) ;
23 r e t u r n tmp ;
24 }
25 }