440 likes | 546 Vues
This lecture delves into synchronization programming, focusing on classic problems such as the Producer-Consumer problem, Reader-Writer problem, Dining Philosophers problem, and the Barber problem. The goal is to familiarize students with the concepts and challenges of synchronization. Practical examples, like simulating a donut production and consumption scenario, illustrate key concepts. Discussion on common mistakes and effective solutions using mutexes and semaphores will enhance understanding. This foundational knowledge is critical for successful concurrent programming.
E N D
CS 241 Lecture 15 9/29/2006 Synchronization Programming Yuanyuan Zhou Chapters: R &R 550-564, Stallings Chapter A.3
Lecture Quiz • Answer bubbles must be carefully shaded in pencil. • Put your Last name, Network ID, and student number (UID from student card) in bubble sheet. • Turn sheet over and answer each question for the given question numbers • Make sure you submit to the TA
Administrative • This week • MP quiz quizmp3 in Discussion Sections or office hours • SMP4 due Sunday at 10:00pm. • Self Assessment Quiz Wk 6
Content of This Lecture Goals: Having you familiar and comfortable with synchronization programming How? Classic synchronization problems Producer-consumer problem Reader-writer problem Dinning philosopher problem Barber problem Common mistakes
Producer-Consumer Problem • Problem description • A producer: in an infinite loop and produce one item each iteration into the buffer • A consumer: in an infinite loop and consumes one item each iteration from the buffer • Buffer size: can only hold at most N items • Real world example • Web server • Producer: dispatcher threads • Consumer: worker threads
Need 4 volunteers • 1 Consumer • Consume donuts following the program (using step papers) • Each time only one step • 1 Producer • Produce donuts following theprogram ((using step papers) • Each time only one step • 2 Schedulers • Cannot run a process forever • Switch from one to another after 1 or many steps • Try to create some error/deadlock ASAP • Minimize #donuts consumed • Then can eat some donut
Producer-Consumer Program int counter; //initialize to 0 // Producer repeat • read the counter value loud; • if(Counter < 4) { // say it loudly • increment the counter loud; • update the counter with the incremented value; • put a donut onto a plate; //ERROR if no more empty plate}else{ • yield to the scheduler for this turn;} Until YY says stop // consumer repeat • read the counter value loud; • if(Counter > 0) { // say it loudly • decrement counter loud; • update the counter with the incremented value; • consume the donut from a plate; //ERROR if no more donut}else{ • yield to the scheduler for this turn;} Until YY says stop
Producer-Consumer Program int counter; //initialize to 0 Mutex m; // Producer repeat • Mutex_Lock(&m); • read counter value on the board loud; • if(Counter < 4) { // say it loudly • increment the counter loud; • update the counter with the incremented value; • put a donut onto a plate; //ERROR if no more empty plate • Mutex_Unlock(&m)}else{ • yield to the scheduler for this turn;} Until YY says stop // consumer repeat • Mutex_Lock(&m); • read counter value on the board loud; • if(Counter > 0) { // say it loudly • decrement counter loud; • update the counter with the incremented value; • consume the donut from a plate; //ERROR if no more donutMutex_Unlock(&m)}else{ • yield to the scheduler for this turn;} Until YY says stop
Audience: How to fix it? (also donut) int counter; //initialize to 0 Mutex m; // Producer repeat • Mutex_Lock(&m); • read counter value on the board loud; • if(Counter < 4) { // say it loudly • increment the counter loud; • update the counter with the incremented value; • put a donut onto a plate; //ERROR if no more empty plate • Mutex_Unlock(&m);}else{ • yield to the scheduler for this turn;} Until YY says stop // consumer repeat • Mutex_Lock(&m); • read counter value on the board loud; • if(Counter > 0) { // say it loudly • decrement counter loud; • update the counter with the incremented value; • consume the donut from a plate; //ERROR if no more donut • Mutex_Unlock(&m);}else{ • yield to the scheduler for this turn;} Until YY says stop
Correct Implementation: Last Demo int counter; //initialize to 0 Mutex m; // Producer repeat • Mutex_Lock(&m); • read counter value on the board loud; • if(Counter < 4) { // say it loudly • increment the counter loud; • update the counter with the incremented value; • put a donut onto a plate; //ERROR if no more empty plate • Mutex_Unlock(&m);}else{ • Mutex_Unlock(&m); • yield to the scheduler for this turn;} Until YY says stop // consumer repeat • Mutex_Lock(&m); • read counter value on the board loud; • if(Counter > 0) { // say it loudly • decrement counter loud; • update the counter with the incremented value; • consume the donut from a plate; //ERROR if no more donut • Mutex_Unlock(&m);}else{ • Mutex_Unlock(&m); • yield to the scheduler for this turn;} Until YY says stop
Another Solution using Semaphores Semaphore Full; //initialize to 0 Semaphore Empty; //initialize to 4 Mutex m; // Producer repeat • P(&empty) • Mutex_lock(&m); • put a donut onto a plate; • Mutex_Unlock(&m); • V(&full); Until YY says stop // consumer repeat • P(&full) • Mutex_lock(&m); • get a donut from a plate; • Mutex_Unlock(&m); • V(&empty); Until YY says stop
First Reader-Writer Problem • readers: read data • writers: write data • Rule: • Multiple readers can read the data simultaneously • Only one writer can write the data at any time • A reader and a writer cannot in critical section together. • Locking table: whether any two can be in the critical section simultaneously
First Reader-Writer Solution • Does it work? What if? • Problem with this solution Mutex m, wrt; int readcount; // shared and initialized to 0 // Writer // Reader Lock(&m); readcount:=readcount+1; Lock(&wrt); if (readcount == 1) lock(&wrt); ...... unlock(&m); writing performed .... ..... reading performed lock(&m); Lock(&wrt); readcount:=readcount-1; if (readcount == 0) unlock(&wrt); Unlock(&m);
Dining Philosophers: an intellectual game • Philosophers eat/think • Eating needs 2 forks • Pick one fork at a time
Need 5 volunteers • 3 Philosophers • Follow the program to • Think • Eat a donut with 2 forks • 2 Schedulers • Cannot run a process forever • Can switch from one to another after 1 or many steps • Try to create some error/deadlock ASAP • Minimize #donuts consumed • Then can eat some donut
Demo 1 Philosopher() { repeat • think; //say something intelligent • take left hand fork; • take right hand fork; • eat one bite; • put left hand fork; • put right hand fork; Until YY says stop; }
Demo 2: Will it work? Mutex m; Philosopher() { repeat • think; //say something intelligent • Mutex_Lock(&m); • take left hand fork; • take right hand fork; • Mutex_Unlock(&m); • eat one bite;Mutex_Lock(&m); • put left hand fork; • put right hand fork; • Mutex_Unlock(&m); Until YY says stop; }
Dining Philosophers Solution Please give an implementation of take_forks() and put_forks();
The Sleeping Barber Problem • N customer Chairs • One barber can cut one customer’s hair at any time • No customer, goes to sleep
The Sleeping Barber Solution (1) Please give an implementation of Barber() and Customer();
The Sleeping Barber Solution (3) Solution to sleeping barber problem.
Be Careful When Using Semaphores // Violation of Mutual Exclusion Up(mutex); mutexUnlock(); critical section criticalSection(); Down(mutex); mutexLock(); // Deadlock Situation Down(mutex); mutexLock(); critical section criticalSection(); Down(mutex); mutexLock(P); // Violation of Mutual Exclusion (omit wait(mutex)/mutexLock()) critical section critical Section(); Up(mutex); mutexUnlock(); // Deadlock Situation (omit signal(mutex)/mutexUnlock()) Down(mutex); mutexLock(); critical section criticalSection();
Summary Classic synchronization problems Producer-consumer Read-writer Dinning philosopher Sleeping barber
bufin bufout Producer Consumer Problem #include <pthread.h> #include "buffer.h" static buffer_t buffer[BUFSIZE]; static pthread_mutex_t bufferlock = PTHREAD_MUTEX_INITIALIZER; static int bufin = 0; static int bufout = 0;
Producer Consumer Problem int getitem(buffer_t *itemp) { /* remove item from buffer and put in *itemp */ int error; if (error = pthread_mutex_lock(&bufferlock)) /*no mutex, give up*/ return error; *itemp = buffer[bufout]; bufout = (bufout + 1) % BUFSIZE; return pthread_mutex_unlock(&bufferlock); } int putitem(buffer_t item) { /* insert item in the buffer */ int error; if (error = pthread_mutex_lock(&bufferlock)) /*no mutex, give up*/ return error; buffer[bufin] = item; bufin = (bufin + 1) % BUFSIZE; return pthread_mutex_unlock(&bufferlock); }
bufin bufout Producer Consumer using Semaphores #include <errno.h> #include <pthread.h> #include <semaphore.h> #include "buffer.h" static buffer_t buffer[BUFSIZE]; static int bufin = 0; static int bufout = 0; static sem_t mutexbuffer; static sem_t semitems; static sem_t semslots; Note: #semitems+#semslots=BUFSIZE
Producer Consumer Semaphores int bufferinit(void) { /*call this exactly once BEFORE getitem and putitem */ int error; if (sem_init(&semitems, 0, 0)) return errno; if (sem_init(&semslots, 0, BUFSIZE)) { error = errno; sem_destroy(&semitems); /* free the other semaphore */ return error; } if (sem_init(&mutexbuffer, 0, 1)) { error = errno; sem_destroy(&semitems); sem_destroy(&semitems); return error; } return 0; }
Producer Consumer Semaphores int getitem(buffer_t *itemp) { /* remove item from buffer and put in *itemp */ int error; while (((error = sem_wait(&semitems)) == -1) && (errno == EINTR)) ; if (error) return errno; if (error = sem_wait(&mutexbuffer)) return error; *itemp = buffer[bufout]; bufout = (bufout + 1) % BUFSIZE; if (error = sem_post(&mutexbuffer)) return error; if (sem_post(&semslots) == -1) return errno; return 0; }
Producer Consumer Semaphores int putitem(buffer_t item) { /* insert item in the buffer */ int error; while (((error = sem_wait(&semslots)) == -1) && (errno == EINTR)) ; if (error) return errno; if (error = sem_wait(&mutexbuffer)) return error; buffer[bufin] = item; bufin = (bufin + 1) % BUFSIZE; if (error = sem_post(&mutexbuffer)) return error; if (sem_post(&semitems) == -1) return errno; return 0; }
Counter example #include <pthread.h> static int count = 0; static pthread_mutex_t countlock = PTHREAD_MUTEX_INITIALIZER; int increment(void) { /* increment the counter */ int error; if (error = pthread_mutex_lock(&countlock)) return error; count++; return pthread_mutex_unlock(&countlock); }
Counter Example Cont. int decrement(void) { /* decrement the counter */ int error; if (error = pthread_mutex_lock(&countlock)) return error; count--; return pthread_mutex_unlock(&countlock); } int getcount(int *countp) { /* retrieve the counter */ int error; if (error = pthread_mutex_lock(&countlock)) return error; *countp = count; return pthread_mutex_unlock(&countlock); }
Thread safe library calls int randsafe(double *ranp); #include <pthread.h> #include <stdlib.h> int randsafe(double *ranp) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; int error; if (error = pthread_mutex_lock(&lock)) return error; *ranp = (rand() + 0.5)/(RAND_MAX + 1.0); return pthread_mutex_unlock(&lock); }
Using a synchronization flag #include <pthread.h> static int doneflag = 0; static pthread_mutex_t donelock = PTHREAD_MUTEX_INITIALIZER; int getdone(int *flag) { /* get the flag */ int error; if (error = pthread_mutex_lock(&donelock)) return error; *flag = doneflag; return pthread_mutex_unlock(&donelock); } int setdone(void) { /* set the flag */ int error; if (error = pthread_mutex_lock(&donelock)) return error; doneflag = 1; return pthread_mutex_unlock(&donelock); }
Thread Safety • Libraries that have static data • Error reports that print on console • Shared data structures like lists