进程同步之熟睡的理发师问题

熟睡的理发师问题(The Sleeping-Baber Problem)是操作系统中关于进程同步的一个经典问题,它涉及了到临界区保护、锁、信号量等方面的知识。在这篇博客中,我将具体讲解这个问题并用pthread库编码解决,相关pthread库的使用可查阅IEEE官方文档,我是传送门~~~

问题描述:
 某个理发店有个瞌睡虫理发师,只要没有顾客,他就会去睡觉;而整个理发店由两个区域组成:有N把椅子的等候区和一把椅子的理发区。
 每当有一个顾客到来时,他有以下三种选择:
   1. 理发师在睡觉,则叫醒理发师并开始理发;
   2. 理发师在忙但等候区有空位,则进入等候区等候;
   3.理发师在忙而等候区又满了,则掉头离开;
 现在请你想一个办法帮助协调理发师和顾客。

要解决这个问题,我们先分析一下这个问题中将存在的一些的特殊情况:
 1. Deadlock:理发师给一个顾客理完发后没有人通知他后面还有顾客,于是理发师去睡觉而顾客一直等待,形成死锁;
 2. Starvation:要是某些顾客来的比较巧,刚好碰到理发师给一个顾客理完发,于是理发师连忙给新来的这个顾客理发而不顾忌等候区的顾客,可能造成等候区的顾客无限的等待下去;
 3. Critical Section:多个新来的顾客同时抢占椅子或同时又有一个顾客去理发,这便会涉及到椅子这个资源的分配问题,因此椅子便是这个问题的临界区资源。

 在解决Deadlock和Starvation问题时,我们可以通过维护两个信号量(顾客和理发师)来解决,;而Critical Section问题就使用一把互斥锁解决。

// 定义p、v操作
#define p(x) sem_wait(&x)
#define v(x) sem_post(&x)
// 初始化理发师和顾客信号量
sem_t baber, customers;
sem_init(&baber, 0, 1); // 理发师初始为1
sem_init(&customers, 0, 0)// 顾客初始为0
// 互斥信号量 保护临界区(即椅子)
pthread_mutex_t chair_mutex;


 所以理发师这边的逻辑大致如下:

p(customers);       // 查看顾客等候区(信号量),小于0就去睡觉(线程阻塞)

lock(chair_mutex);  // 顾客起身去理发,获得椅子读写的锁
++chair;            
unlock(chair_mutex);// 释放锁

// 给顾客理发...
v(baber);           // 理完一个,通知等候区的顾客自己已经空闲了

 而顾客的逻辑大致如下:

lock(chair_mutex);  // 刚到的顾客,获得椅子读写的锁,判断是否还有空位
if(chair > 0) {     // 有空位
    --chair;
    v(customers);   // 通知理发师队列有了新顾客
    unlock(chair_mutex);  // 释放锁
    p(baber);       // 等待理发师,阻塞线程
    // 理发...
    // 离开...
} else {            // 无空位  
    unlock(chair_mutex);  // 释放锁
    // 离开...
}

 通过实现以上这两种逻辑,使得理发师在每次睡觉之前将检查一下等候区是否还有顾客在等待(p(customer)),在给一个顾客理完发后又会通知等候区的顾客(v(baber));而每有新的顾客到来时,顾客又将通知理发师(v(customer)),然后去等候区队列的末尾等待理发师(p(baber));同时,使用互斥锁对椅子进行保护,防止了椅子被同时抢占造成资源的不同步。

  接下来给出设定一共有20个顾客先后来到,而只有5把椅子的运行结果(完整代码见末尾):

the baber shop opens...

customer #0 comes...
the chair left : 4
customer #0 is getting a hair cut...
the baber is working on one...
so the chair left : 5
the baber has done one!
customer #1 comes...
the chair left : 4
customer #1 is getting a hair cut...
the baber is working on one...
so the chair left : 5
the baber has done one!
customer #2 comes...
the chair left : 4
customer #2 is getting a hair cut...
customer #3 comes...
the chair left : 3
the baber is working on one...
so the chair left : 4
the baber has done one!
customer #3 is getting a hair cut...
customer #4 comes...
the chair left : 3
customer #5 comes...
the chair left : 2
the baber is working on one...
so the chair left : 3
the baber has done one!
customer #4 is getting a hair cut...
customer #6 comes...
the chair left : 2
customer #7 comes...
the chair left : 1
the baber is working on one...
so the chair left : 2
the baber has done one!
customer #5 is getting a hair cut...
customer #8 comes...
the chair left : 1
customer #9 comes...
the chair left : 0
the baber is working on one...
so the chair left : 1
the baber has done one!
customer #6 is getting a hair cut...
customer #10 comes...
the chair left : 0
customer #11 comes...
customer #11 left...
the baber is working on one...
so the chair left : 1
the baber has done one!
customer #7 is getting a hair cut...
customer #12 comes...
the chair left : 0
customer #13 comes...
customer #13 left...
the baber is working on one...
so the chair left : 1
the baber has done one!
customer #8 is getting a hair cut...
customer #14 comes...
the chair left : 0
customer #15 comes...
customer #15 left...
the baber is working on one...
so the chair left : 1
the baber has done one!
customer #9 is getting a hair cut...
customer #16 comes...
the chair left : 0
customer #17 comes...
customer #17 left...
the baber is working on one...
so the chair left : 1
the baber has done one!
customer #10 is getting a hair cut...
customer #18 comes...
the chair left : 0
customer #19 comes...
customer #19 left...
the baber is working on one...
so the chair left : 1
the baber has done one!
customer #12 is getting a hair cut...
the baber is working on one...
so the chair left : 2
the baber has done one!
customer #14 is getting a hair cut...
the baber is working on one...
so the chair left : 3
the baber has done one!
customer #16 is getting a hair cut...
the baber is working on one...
so the chair left : 4
the baber has done one!
customer #18 is getting a hair cut...
the baber is working on one...
so the chair left : 5
the baber has done one!

 从以上结果可以看出,之前的逻辑完美解决了理发师和顾客间存在的Deadlock,Stravation和Critical Section的问题。

完整代码

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

#define TRUE 1
#define FALSE 0

// 最大椅子数
#define MAX_CHAIR 5
// 最大顾客数
#define MAX_CUSTOMERS 20

// 定义p、v操作
#define p(x) sem_wait(&x)
#define v(x) sem_post(&x)

// 椅子
int chair;
// 理发师和顾客信号量
sem_t baber, customers;
// 互斥信号量 保护临界区(即椅子)
pthread_mutex_t chair_mutex;

// 初始化椅子数和信号量
int init()
{
    chair = MAX_CHAIR;
    return (sem_init(&baber, 0, 1) || sem_init(&customers, 0, 0));
}

// 理发师线程函数
void* _baber(void *arg)
{
    printf("the baber shop opens...\n\n");
    while (TRUE)
    {
        p(customers); // 尝试为一位顾客服务,否则睡觉
        // printf("baber wake up...\n");
        pthread_mutex_lock(&chair_mutex);
        ++chair;    // 一个顾客去理发,空出一个椅子
        printf("the baber is working on one...\nso the chair left : %d\n", chair);
        pthread_mutex_unlock(&chair_mutex);
        v(baber);   // 理发师理完一个
        printf("the baber has done one!\n");
        sleep(2);
    }
}

// 顾客线程函数
void* _customer(void *arg)
{
    int * id_p = (int *)arg;
    int id = *id_p;

    printf("customer #%d comes...\n", id);

    pthread_mutex_lock(&chair_mutex); // 想获得椅子
    if(chair > 0) {   // 还有空椅子
        --chair;
        v(customers); // 新加入一个顾客
        printf("the chair left : %d\n", chair);
        pthread_mutex_unlock(&chair_mutex);
        p(baber);     //等待理发师
        printf("customer #%d is getting a hair cut...\n", id);
        sleep(1);
    }
    else
    {
        pthread_mutex_unlock(&chair_mutex); // 释放锁
        printf("customer #%d left...\n", id);
    }
}

int main()
{
    if(init()) {
        printf("initialize semaphore error!\n");
        return 0;
    }

    pthread_t baber_tid;
    pthread_t customers_tid[MAX_CUSTOMERS];

    pthread_attr_t attr;
    pthread_attr_init(&attr);

    pthread_create(&baber_tid, &attr, _baber, NULL);

    for (int i = 0; i < MAX_CUSTOMERS; ++i) {
        pthread_create(&customers_tid[i], &attr, _customer, (void *)&i);
        sleep(1);
    }

    pthread_join(baber_tid, NULL);
    for (int i = 0; i < MAX_CUSTOMERS; ++i)
        pthread_join(customers_tid[i], NULL);

    return 0;
}

 代码下载

 (完)

没有更多推荐了,返回首页