嗜睡的理发师问题:一个理发店由一个有N张沙发的等候室和一个放有一张理发椅的理发室组成。没有顾客要理发时,理发师便去睡觉。当一个顾客走进理发店时,如果所有的沙发都已经被占用,他便离开理发店;否则,如果理发师正在为其他顾客理发,则该顾客就找一张空沙发坐下等待;如果理发师因无顾客正在睡觉,则由新到的顾客唤醒理发师为其理发。在理发完成后,顾客必须付费,直到理发师收费后才能离开理发店。帮帮忙,用信号量实习这个同步问题。
实现上面的效果,可以使用是“生产者-消费者-仓储”模型,需要实现以下几点:
1、顾客仅仅在座位未满时候入座,座位满了满则停止入店。
2、理发师仅仅在座位有顾客时候才能消费,座位空则等待。
3、当理发师发现座位没顾客可服务时候会通知顾客进店落座。
4、顾客在落座后,也需要通知睡觉的理发师来服务。
具体实现代码如下:
第一个是.hpp代码:
#pragma once
#include <iostream>
#include <vector>
#include <semaphore.h>
#include<pthread.h>
using namespace std;
namespace ns_cq
{
pthread_mutex_t p_mtx;
pthread_mutex_t c_mtx;
const int default_cap = 10;
template <class T>
class CircularQueue
{
private:
int _cap;
vector<T> _circul_queue;
//顾客关心空位置的资源
sem_t _blank_sem;
//理发师关心位置有顾客的资源
sem_t _data_sem;
//用vector模拟环形队列
int _c_step;
int _p_step;
public:
CircularQueue(int cap = default_cap)
: _cap(cap), _circul_queue(cap)
{
//顾客信号量初始化,初始时顾客能申请的信号量为10,因为有10个空位置
sem_init(&_blank_sem, 0, _cap);
//理发师信号量初始化,初始时理发师申请到的信号量为0,因为没有顾客坐位置
sem_init(&_data_sem, 0, 0);
_c_step = 0;
_p_step = 0;
pthread_mutex_init(&p_mtx, nullptr);
pthread_mutex_init(&c_mtx, nullptr);
}
~CircularQueue()
{
//信号量销毁
sem_destroy(&_blank_sem);
sem_destroy(&_data_sem);
pthread_mutex_destroy(&p_mtx);
pthread_mutex_destroy(&c_mtx);
}
public:
void Push(const T &in)
{
//客户申请信号量(P操作) 相当于空座位的计数器--
sem_wait(&_blank_sem);
//把锁加在申请信号量后,能让多个线程并发申请信号量,然后再竞争锁,省去了单独申请信号量的时间,效率提高
pthread_mutex_lock(&p_mtx);
_circul_queue[_p_step] = in;
//客户做到空座位后,更新已做人座位的下标
_p_step++;
_p_step %= _cap;
pthread_mutex_unlock(&p_mtx);
//释放信号量(V操作) 相当于有客户坐的座位的计数器++
sem_post(&_data_sem);
}
void Pop(T *out)
{
//理发师申请信号量(P操作) 相当于有客户坐的座位的计数器--
sem_wait(&_data_sem);
pthread_mutex_lock(&c_mtx);
*out = _circul_queue[_c_step];
_c_step++;
_c_step %= _cap;
pthread_mutex_unlock(&c_mtx);
//释放信号量(V操作) 相当于空座位的计数器++
sem_post(&_blank_sem);
}
};
}
第二个是.cpp代码:
#include <iostream>
using namespace std;
#include "Circular_queue.hpp"
using namespace ns_cq;
using namespace ns_task;
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include "task.hpp"
void *Consumer(void *args)
{
CircularQueue<Task> *cq = (CircularQueue<Task> *)args;
while (true)
{
// sleep(1);
Task t;
cq->Pop(&t);
t.Run();
sleep(1);
}
}
void *Producter(void *args)
{
CircularQueue<Task> *cq = (CircularQueue<Task> *)args;
while (true)
{
sleep(1);
cout << " 有一个顾客入店落座了!快醒醒" << endl;
cq->Push(t);
}
}
int main()
{
srand((long long)time(nullptr));
pthread_t c0, c1, c2, c3, p0, p1, p2, p3;
CircularQueue<Task> *cq = new CircularQueue<Task>();
pthread_create(&c0, nullptr, Consumer, (void *)cq);
pthread_create(&c1, nullptr, Consumer, (void *)cq);
pthread_create(&c2, nullptr, Consumer, (void *)cq);
pthread_create(&c3, nullptr, Consumer, (void *)cq);
pthread_create(&p0, nullptr, Producter, (void *)cq);
pthread_create(&p1, nullptr, Producter, (void *)cq);
pthread_create(&p2, nullptr, Producter, (void *)cq);
pthread_create(&p3, nullptr, Producter, (void *)cq);
pthread_join(c0, nullptr);
pthread_join(c1, nullptr);
pthread_join(c2, nullptr);
pthread_join(c3, nullptr);
pthread_join(p0, nullptr);
pthread_join(p1, nullptr);
pthread_join(p2, nullptr);
pthread_join(p3, nullptr);
return 0;
}