进程同步与互斥——理发师问题(实现OS上的伪代码)

理发师问题描述:

(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;
}

上一篇:JUC并发编程(1)


下一篇:Java中多线程的六种状态详解