理发师问题描述:
(1)理发店里有一位理发师、一把理发椅和n把供等候理发的顾客坐的椅子
(2)如果没有顾客,理发师便在理发椅上睡觉
(3)一个顾客到来时,它必须叫醒理发师
(4)如果理发师正在理发时又有顾客来到,则如果有空椅子可坐,就坐下来等待,否则就离开
问题分析:
1、对于理发师问题而言,是生产者-消费者(有界缓冲区)模型的一种。其中理发师和顾客之间涉及到进程之间的同步问题,理发师是生产者,顾客是消费者,生产者生产的速度(理发师理发的速度),和消费者消费的速度(顾客来到理发店的时间),这两者肯定是不同的。那么,题目中就涉及到了一个有界缓冲区,即有N把顾客可以坐着等候理发师的椅子。如果顾客来的太快了,就可以先坐在椅子上等候一下理发师,但是如果椅子坐满了,这时候顾客就直接走,不理发了。这个N是有界缓冲区的大小,如果缓冲区放不下消费者了,消费者就不进行消费。
2、同样的,在生产者-消费者(有界缓冲区)模型中,还存在进程之间的互斥,比如多个消费者同时访问缓冲区,那么肯定会改变缓冲区的状态,缓冲区就是临界资源,多个消费者不能同时去改变缓冲区的状态。在这个问题上,就相当于执行顾客任务的进程,就必须有互斥的操作,同样的,理发师改变缓冲区状态的操作也需要互斥。这个问题中,缓冲区的状态,就是还剩多少个在等待的顾客,顾客来一个,肯定等待理发的顾客数目就+1,理发师理一次发,等待理发的顾客数目就-1。
我们先确定初始状态,即:
顾客没来的时候理发师正在睡觉,刚开始也没有顾客来:
semaphore barbers = 0;
semaphore customers = 0;
顾客之间被理发的时候,也需要一个互斥量:
semaphore mutex = 1;
初始化有界缓冲区的大小是N,即
#define CHAIRS 5
另外,我们还需要一个变量来记录目前缓冲区的大小是多少,看现在正在等待的顾客数量有没有超过缓冲区的大小,如果超过了,顾客就之间转身走了。
int waiting = 0;
以上,初始化的操作已经完成了。
#define CHAIRS 5
semaphore customers = 0;
semaphore barbers = 0;
semaphore mutex = 1;
int waiting = 0;
然后,考虑理发师进程需要执行的任务,首先理发师是不断进行理发操作的,只有一个生产者,所以肯定对任务有while(1)让它不断执行的操作。
在单次任务中,理发师先等待顾客,如果顾客没有来,理发师就在睡觉。
P(consumers);
然后,如果等到了一个顾客来了,理发师就要更新缓冲区状态了,把正在等待理发的顾客数目-1,先进行加锁互斥。
P(mutex);
waiting = waiting - 1;
然后理发师睡起来了,准备开始理发了。
V(barber);
因为理发操作,所需花费的时间是很长的,这里不能放在临界区去执行。理发师理发前,先解锁,离开临界区。
V(mutex)
cut_hair();
那么,理发师进程需要执行的任务,我们就可以写出来了:
void barber(void)
{
white (TRUE) {
P(customers); //若无顾客,理发师睡眠
P(mutex); //进程互斥,要求顾客等候
waiting = waiting − 1; //等候顾客数少一个
V(barbers); //理发师去为一个顾客理发
V(mutex); //开放临界区
cut_hair(); //正在理发(非临界区操作)
}
}
同样,再考虑顾客的任务,对于顾客来说,每次来的一个顾客就是一个单独的任务。
首先,顾客来了,要判断现在还有没有位置可以坐下,如果没有,就直接走了。对于waiting < CHAIRS的判断必须在临界区内进行判断,每次只能有一个顾客。如果出现10个顾客同时判断,这时候都是waiting < CHAIRS,那这10个顾客都能坐在了,但实际座位只有5个。
P(mutex)
if(waiting < CHAIRS)
{
}
else
{
V(mutex);
cout << "现在没有位置,顾客转身就走了" << endl;
}
然后,每个顾客来之后,如果发现现在还有位置坐,就先坐下来,然后正在等待的顾客数量+1,并且告诉理发师,现在有顾客来了,唤醒理发师,然后就阻塞在顾客等待理发理发的队列上,等待理发师理发。
P(mutex)
if(waiting < CHAIRS)
{
waiting = waiting+1; // 等候顾客数加1
V(customers); //必要的话唤醒理发师
V(mutex); //开放临界区
P(barbers); //理发师在忙,顾客等待
get-haircut( ); //一个顾客坐下等候服务
}
else
{
V(mutex);
cout << "现在没有位置,顾客转身就走了" << endl;
}
OS上给的伪代码:
#define CHAIRS 5 /* # chairs for waiting customers */
typedef int semaphore; /* use your imagination */
semaphore customers = 0; /* # of customers waiting for service */
semaphore barbers = 0; /* # of barbers waiting for customers */
semaphore mutex = 1; /* for mutual exclusion */
int waiting = 0; /* customers are waiting (not being cut) */
void barber(void)
{
white (TRUE) {
down(&customers); /* go to sleep if # of customers is 0 */
down(&mutex); /* acquire access to 'waiting' */
waiting = waiting − 1; /* decrement count of waiting customers */
up(&barbers); /* one barber is now ready to cut hair */
up(&mutex); /* release 'waiting' */
cut_hair(); /* cut hair (outside critical region) */
}
}
void customer(void)
{
down(&mutex); /* enter critical region */
if (waiting < CHAIRS) { /* if there are no free chairs, leave */
waiting = waiting + 1; /* increment count of waiting customers */
up(&customers); /* wake up barber if necessary */
up(&mutex); /* release access to 'waiting' */
down(&barbers); /* go to sleep if # of free barbers is 0 */
get_haircut(); /* be seated and be serviced */
} else {
up(&mutex); /* shop is full; do not wait */
}
}
实现:
#include <semaphore.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
#include <iostream>
#include <queue>
using namespace std;
typedef sem_t semaphore;
queue<int> wait_queue;
#define CHAIRS 5
int waiting = 0;
semaphore barber;
semaphore consumers;
semaphore mutexlock;
void* barber_process(void*)
{
while(1)
{
int id;
sem_wait(&consumers);
sem_wait(&mutexlock);
waiting = waiting - 1;
id = wait_queue.front();
wait_queue.pop();
sem_post(&barber);
sem_post(&mutexlock);
cout << "理发师正在给第" << id << "位等待的顾客理发" << endl;
srand(time(0));
/* sleep(rand() % 3 + 2); */
sleep(3);
}
}
void* consumer_process(void* p)
{
int i = *(int*)p;
sem_wait(&mutexlock);
if(waiting < CHAIRS)
{
wait_queue.push(i);
waiting = waiting + 1;
sem_post(&consumers);
sem_post(&mutexlock);
sem_wait(&barber);
cout << "第" << i << "位顾客来了,正在接受理发师的理发服务" << endl;
cout << "正在等待理发师理发的顾客还有" << waiting << "位" << endl << endl;
}
else
{
sem_post(&mutexlock);
cout << "门外的第" << i << "位顾客看见没有椅子就转身走了" << endl << endl;
}
pthread_exit(0);
}
int main()
{
pthread_t p_barber;
pthread_t p_consumers[10];
int num_consumers = 20;
sem_init(&barber, 0 ,0);
sem_init(&consumers, 0, 0);
sem_init(&mutexlock, 0, 1);
pthread_create(&p_barber, nullptr, barber_process, nullptr);
for(int i = 0; i < num_consumers; i++)
{
pthread_create(&p_consumers[i], nullptr, consumer_process, &i);
srand(time(0));
sleep(rand() % 2 + 1);
}
for(int i = 0; i < num_consumers; i++)
{
pthread_join(p_consumers[i], nullptr);
}
sleep(5);
return 0;
}