可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
The Sleeping Barber Problem is a classical synchronization problem that many of you may be familiar with or at least heard of.
It's based on the premise that a barber (a thread) sleeps when there are no customers (each customer is a thread) in the waiting room (which is a semaphore). If there is someone, he cuts his hair (symbolizing some processing) and the costumer leaves. If, then, there is no one in the waiting room, the barber goes to sleep. When another customer arrives, he then must wake up the barber.
I have made a implementation of this using the following basic-idea
(although the actual code is in C, I wrote the following pseudo-code without much care for the syntax for sake of problem-understading, only using sem_wait and sem_post1 for smoother reading)
Semaphore Customers = 0; Semaphore Barber = 0; Mutex accessSeats = 1; int NumberOfFreeSeats = N; Barber { while(1) { sem_wait(Customers); // waits for a customer (sleeps) sem_wait(accessSeats); // mutex to protect the number of available seats NumberOfFreeSeats++; // a chair gets free sem_post(Barber); // bring customer for haircut sem_post(accessSeats); // release the mutex on the chair // barber is cutting hair } } Customer { while(1) { sem_wait(accessSeats); // protects seats so only 1 thread tries to sit in a chair if that's the case if(NumberOfFreeSeats > 0) { NumberOfFreeSeats--; // sitting down sem_post(Customers); // notify the barber sem_post(accessSeats); // release the lock sem_wait(Barber); // wait in the waiting room if barber is busy // customer is having hair cut } else { sem_post(accessSeats); // release the lock // customer leaves } } }
However, now that I'll implement this problem with multiple barbers, my head got stuck. I went on Wikipedia to see if I could find something about it, but the only thing that I found there was this
A multiple sleeping barbers problem has the additional complexity of coordinating several barbers among the waiting customers.
and I couldn't figure this out by myself 2.
What changes would I have to do in my code? Where would I need an extra semaphore here?
1sem_wait() locks the semaphore. sem_post() unlocks it
2Disclaimer: Although I have asked this in programmers.stackexchange aswell, I did not have reached a desired answer and my question still persists.
回答1:
The code as written will manage any number of barbers without any additional semaphores. Simply start a Barber{} thread for each barber in the shop.
The problem referenced in the Wikipedia remark is this: just because you have M barbers in the "barber is cutting hair" state and M customers in the "customer is having hair cut" state, there's no guarantee that some barber isn't trying to clip more than one customer, or that some customer doesn't have several barbers in his hair.
In other words, once the proper number of customers have been allowed into the cutting room, how do the barbers and customers pair off? You can no longer say "barber is cutting hair" and "customer is having hair cut"; you have to say "barber is cutting hair of customer C" and "customer is having hair cut by barber B".
// Assumptions about the meaning of 'haircut': // each thread has a unique PID // each thread can get its own PID via system.info.PID // a barber uses a customer's PID to cut that custmer's hair // a customer uses a barber's PID to get his hair cut by that barber // Assumptions about the operating environment: // a semaphore releases threads in the order that they were queued Semaphore Customers = 0; Semaphore Barbers = 0; Mutex AccessSeats = 1; int NumberOfFreeSeats = N; int SeatPocket[N]; // (or 'PID SeatPocket[N];' if PID is a data type) int SitHereNext = 0; int ServeMeNext = 0; // main program must start a copy of this thread for each barber in the shop Barber { int mynext, C; while(1) { sem_wait(Barbers); // join queue of sleeping barbers sem_wait(AccessSeats); // mutex to protect seat changes ServeMeNext = (ServeMeNext++) mod N; // select next customer mynext = ServeMeNext; C = SeatPocket[mynext]; // get selected customer's PID SeatPocket[mynext] = system.info.PID; // leave own PID for customer sem_post(AccessSeats); // release the seat change mutex sem_post(Customers); // wake up selected customer // // barber is cutting hair of customer 'C' // } } // main program must start a copy of this thread at random intervals // to represent the arrival of a continual stream of customers Customer { int myseat, B; sem_wait(AccessSeats); // mutex to protect seat changes if(NumberOfFreeSeats > 0) { NumberOfFreeSeats--; // sit down in one of the seats SitHereNext = (SitHereNext++) mod N; myseat = SitHereNext; SeatPocket[myseat] = system.info.PID; sem_post(AccessSeats); // release the seat change mutex sem_post(Barbers); // wake up one barber sem_wait(Customers); // join queue of sleeping customers sem_wait(AccessSeats); // mutex to protect seat changes B = SeatPocket[myseat]; // barber replaced my PID with his own NumberOfFreeSeats++; // stand up sem_post(AccessSeats); // release the seat change mutex // // customer is having hair cut by barber 'B' // } else { sem_post(AccessSeats); // release the seat change mutex // customer leaves without haircut } system.thread.exit; // (or signal main program to kill this thread) }
回答2:
This code is written by me with slight modifications from the algorithm defined by Breveleri which simulates the Multiple Sleeping Barber problem quite efficiently. I had written it for simulation purpose for my OS Assignment. I would like to get ay suggestions from your side. I'm using "declarations.h", "SleepingBarber.c" and a "makefile" for a better coding structure.
declaration.h
#include <unistd.h> //Provides API for POSIX(or UNIX) OS for system calls #include <stdio.h> //Standard I/O Routines #include <stdlib.h> //For exit() and rand() #include <pthread.h> //Threading APIs #include <semaphore.h> //Semaphore APIs #define MAX_CHAIRS 10 //No. of chairs in waiting room #define CUT_TIME 1 //Hair Cutting Time 1 second #define NUM_BARB 2 //No. of barbers #define MAX_CUST 30 //Maximum no. of customers for simulation sem_t customers; //Semaphore sem_t barbers; //Semaphore sem_t mutex; //Semaphore for providing mutially exclusive access int numberOfFreeSeats = MAX_CHAIRS; //Counter for Vacant seats in waiting room int seatPocket[MAX_CHAIRS]; //To exchange pid between customer and barber int sitHereNext = 0; //Index for next legitimate seat int serveMeNext = 0; //Index to choose a candidate for cutting hair static int count = 0; //Counter of No. of customers void barberThread(void *tmp); //Thread Function void customerThread(void *tmp); //Thread Function void wait(); //Randomized delay function
SleepingBarber.c
#include "declarations.h" int main() { pthread_t barber[NUM_BARB],customer[MAX_CUST]; //Thread declaration int i,status=0; /*Semaphore initialization*/ sem_init(&customers,0,0); sem_init(&barbers,0,0); sem_init(&mutex,0,1); /*Barber thread initialization*/ printf("!!Barber Shop Opens!!\n"); for(i=0;i<NUM_BARB;i++) //Creation of 2 Barber Threads { status=pthread_create(&barber[i],NULL,(void *)barberThread,(void*)&i); sleep(1); if(status!=0) perror("No Barber Present... Sorry!!\n"); } /*Customer thread initialization*/ for(i=0;i<MAX_CUST;i++) //Creation of Customer Threads { status=pthread_create(&customer[i],NULL,(void *)customerThread,(void*)&i); wait(); //Create customers in random interval if(status!=0) perror("No Customers Yet!!!\n"); } for(i=0;i<MAX_CUST;i++) //Waiting till all customers are dealt with pthread_join(customer[i],NULL); printf("!!Barber Shop Closes!!\n"); exit(EXIT_SUCCESS); //Exit abandoning infinite loop of barber thread } void customerThread(void *tmp) /*Customer Process*/ { int mySeat, B; sem_wait(&mutex); //Lock mutex to protect seat changes count++; //Arrival of customer printf("Customer-%d[Id:%d] Entered Shop. ",count,pthread_self()); if(numberOfFreeSeats > 0) { --numberOfFreeSeats; //Sit on chairs on waiting room printf("Customer-%d Sits In Waiting Room.\n",count); sitHereNext = (++sitHereNext) % MAX_CHAIRS; //Choose a vacant chair to sit mySeat = sitHereNext; seatPocket[mySeat] = count; sem_post(&mutex); //Release the seat change mutex sem_post(&barbers); //Wake up one barber sem_wait(&customers); //Join queue of sleeping customers sem_wait(&mutex); //Lock mutex to protect seat changes B = seatPocket[mySeat]; //Barber replaces customer PID with his own PID numberOfFreeSeats++; //Stand Up and Go to Barber Room sem_post(&mutex); //Release the seat change mutex /*Customer is having hair cut by barber 'B'*/ } else { sem_post(&mutex); //Release the mutex and customer leaves without haircut printf("Customer-%d Finds No Seat & Leaves.\n",count); } pthread_exit(0); } void barberThread(void *tmp) /*Barber Process*/ { int index = *(int *)(tmp); int myNext, C; printf("Barber-%d[Id:%d] Joins Shop. ",index,pthread_self()); while(1) /*Infinite loop*/ { printf("Barber-%d Gone To Sleep.\n",index); sem_wait(&barbers); //Join queue of sleeping barbers sem_wait(&mutex); //Lock mutex to protect seat changes serveMeNext = (++serveMeNext) % MAX_CHAIRS; //Select next customer myNext = serveMeNext; C = seatPocket[myNext]; //Get selected customer's PID seatPocket[myNext] = pthread_self(); //Leave own PID for customer sem_post(&mutex); sem_post(&customers); //Call selected customer /*Barber is cutting hair of customer 'C'*/ printf("Barber-%d Wakes Up & Is Cutting Hair Of Customer-%d.\n",index,C); sleep(CUT_TIME); printf("Barber-%d Finishes. ",index); } } void wait() /*Generates random number between 50000 to 250000*/ { int x = rand() % (250000 - 50000 + 1) + 50000; srand(time(NULL)); usleep(x); //usleep halts execution in specified miliseconds }
makefile
all: SleepingBarber SleepingBarber: SleepingBarber.o gcc -pthread -o SleepingBarber SleepingBarber.o SleepingBarber.o: SleepingBarber.c declarations.h gcc -c SleepingBarber.c clean: rm -f *.o SleepingBarber