进程同步与互斥——理发师问题源码实现(sleeping barber problem)

传送门:

进程同步与互斥——信号量(实现锁、条件变量)

进程同步与互斥——哲学家就餐问题源码实现(dining philosopher’s problem)

进程同步与互斥——读者/写者问题源码实现(reader-writer lock)

进程同步与互斥——生产者/消费者(有界缓冲区)问题源码实现

进程同步与互斥——吸烟者问题源码实现(cigarette smoker’s problem)

进程同步与互斥——理发师问题源码实现(sleeping barber problem)

进程同步与互斥——相关问题汇总(源码+伪代码)

理发师问题描述:

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

  • 49
    点赞
  • 244
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
读者与写者问题是多进程并发编程中的经典问题,主要解决的是多个进程同时对共享数据进行读写操作时可能产生的数据不一致性问题。具体来说,该问题假设有若干个读者和一个写者进程,它们同时访问某个共享资源(如一个文件、一个数据库等),读者只读取共享资源而不修改它,而写者则修改共享资源。 由于读者和写者对共享资源的访问方式不同,因此需要对它们的访问进行调度和同步,以防止读写冲突导致数据不一致。下面是一种实现读者与写者问题的方法: ```python from threading import Lock class ReaderWriter: def __init__(self): self.read_count = 0 self.mutex = Lock() # 互斥锁,用于保护共享资源的访问 self.write_lock = Lock() # 写锁,用于保证写操作的原子性 def start_read(self): self.mutex.acquire() self.read_count += 1 if self.read_count == 1: self.write_lock.acquire() # 第一个读者获取写锁,防止写者进程修改共享资源 self.mutex.release() def end_read(self): self.mutex.acquire() self.read_count -= 1 if self.read_count == 0: self.write_lock.release() # 最后一个读者释放写锁,允许写者进程修改共享资源 self.mutex.release() def start_write(self): self.write_lock.acquire() # 写锁保证写操作的原子性 def end_write(self): self.write_lock.release() # 释放写锁 ``` 在上述代码中,`ReaderWriter` 类实现了读者与写者的同步和调度。具体来说,它定义了三个方法: - `start_read`:读者开始读取共享资源时调用该方法,它首先获取互斥锁,然后增加读者计数器,如果是第一个读者,则获取写锁,以防止写者进程修改共享资源。 - `end_read`:读者读取完共享资源时调用该方法,它首先获取互斥锁,然后减少读者计数器,如果是最后一个读者,则释放写锁,允许写者进程修改共享资源。 - `start_write`:写者开始修改共享资源时调用该方法,它直接获取写锁,保证写操作的原子性。 - `end_write`:写者修改完共享资源时调用该方法,它释放写锁。 在实际使用中,可以将共享资源作为 `ReaderWriter` 类的一个属性,并在读者和写者进程中调用相应的方法,以实现进程同步和数据一致性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值