进程同步之理发师问题
来源:互联网 发布:岸谷新罗知乎 编辑:程序博客网 时间:2024/05/05 10:00
进程同步之理发师问题
@(操作系统)[进程同步]
description
假设有一个理发店只有一个理发师,一张理发时坐的椅子,若干张普通椅子顾客供等候时坐。没有顾客时,理发师就坐在理发的椅子上睡觉。顾客一到,他不是叫醒理发师,就是离开。如果理发师没有睡觉,而在为别人理发,他就会坐下来等候。如果所有的椅子都坐满了人,最后来的顾客就会离开。
在出现竞争的情况下问题就来了,这和其它的排队问题是一样的。实际上,与哲学家就餐问题是一样的。如果没有适当的解决方案,就会导致进程之间的“饿肚子”和“死锁”。
如理发师在等一位顾客,顾客在等理发师,进而造成死锁。另外,有的顾客可能也不愿按顺序等候,会让一些在等待的顾客永远都不能理发。
解决方案
最常见的解决方案就是使用三个信号量(Semaphore):一个给顾客信号量,一个理发师信号量(看他自己是不是闲着),第三个是互斥信号量(Mutual exclusion,缩写成mutex)。一位顾客来了,他想拿到互斥信号量,他就等着直到拿到为止。顾客拿到互斥信号量后,会去查看是否有空着的椅子(可能是等候的椅子,也可能是理发时坐的那张椅子)。
如果没有一张是空着的,他就走了。如果他找到了一张椅子,就会让空椅子的数量减少一张,这位顾客接下来就使用自己的信号量叫醒理发师。这样,互斥信号标就释放出来供其他顾客或理发师使用。如果理发师在忙,这位顾客就会等。理发师就会进入了一个永久的等候循环,等着被在等候的顾客唤醒。一旦他醒过来,他会给所有在等候的顾客发信号,让他们依次理发。
PV操作
顾客信号量 = 0 理发师信号量 = 0 互斥信号量mutex = 1 // 椅子是理发师和顾客精进程都可以访问的临界区 int 空椅子数量 = N //所有的椅子数量 理发师(线程/进程) While(true){ //持续不断地循环 P(顾客) //试图为一位顾客服务,如果没有他就睡觉(进程阻塞) P(互斥信号量) //如果有顾客,这时他被叫醒(理发师进程被唤醒),要修改空椅子的数量 空椅子数量++ //一张椅子空了出来 V(理发师) //现在有一个醒着的理发师,理发师准备理发,多个顾客可以竞争理发师互斥量,但是只有一个顾客进程可以被唤醒并得到服务 V(互斥信号量) //释放椅子互斥量,使得进店的顾客可以访问椅子的数量以决定是否进店等待 /* 理发师在理发 */ } 顾客(线程/进程) while(true){ //持续不断地循环 P(互斥信号量) //想坐到一张椅子上 if (空椅子数量 > 0) { //如果还有空着的椅子的话 空椅子数量-- //顾客坐到一张椅子上了 V(顾客) //通知理发师,有一位顾客来了 V(互斥信号量) //顾客已经坐在椅子上等待了,访问椅子结束,释放互斥量 P(理发师) //该这位顾客理发了,如果理发师还在忙,那么他就等着(顾客进程阻塞) /* 竞争到了理发师则该顾客开始理发 */ } else { //没有空着的椅子 V(互斥信号标) //不要忘记释放被锁定的椅子 /* 顾客没有理发就走了 */ } }
c++代码
#include <thread>#include <mutex>#include <iostream>#include <vector>#include <cmath>#include <condition_variable>using namespace std;int chairs = 10;int empty_chairs = chairs; // 空椅子数int customers = 0;int barbers = 1;condition_variable cv_barbers; // 当有顾客到时需通知理发师mutex chairs_mtx, cus_mtx, bar_mtx;/*** 理发师进程,阻塞理发师的请况* 1. 没有顾客,则睡觉(阻塞)* 2. 访问临界区受阻,此时临界区正在被顾客访问* @param i [description]*/void barber(int i){ while (true) { unique_lock<mutex> lck(bar_mtx); cv_barbers.wait(lck, [] { if (customers > 0) { cout << "有顾客,理发师被唤醒" << endl; return true; } else { cout << "没有顾客,理发师睡觉" << endl; return false; } }); unique_lock<mutex> lck2(chairs_mtx); customers--; empty_chairs++; /* cut hair*/ cout << "理发师给顾客理发" << endl; lck2.unlock(); // 理发时不断有顾客进来 this_thread::sleep_for(std::chrono::microseconds(10)); }}/*** 顾客进程,阻塞顾客进程的情况有两种* 1. 访问临界区(检查是否有空闲的椅子)时发现理发师进程也在访问临界区,P(chairs_mtx)* 没有多余的椅子并不是阻塞顾客进程,直至有空闲椅子,而是直接离开,即该顾客不排队理发* @param i [description]*/void customer(int i){ unique_lock<mutex> lck(chairs_mtx); if (empty_chairs > 0) { empty_chairs--; customers++; cv_barbers.notify_one(); cout << "顾客 " << i << " 等待理发" << endl; //this_thread::sleep_for(std::chrono::milliseconds(100)); lck.unlock(); } else { /* 进程退出,不再理发了 */ cout << "顾客 " << i << " 没有位置,离开" << endl; lck.unlock(); /* leave */ }}int main(){ thread t1 = thread(barber, 1); vector<thread> v; for (size_t i = 0; i < 20; i++) { v.push_back(thread(customer, i + 1)); } t1.join(); for (size_t i = 0; i < v.size(); i++) { v[i].join(); } system("pause"); return 0;}
以下是程序运行一种可能的结果:
1. 理发师线程首先被调度,此时没有任何顾客,理发师线程阻塞。
2. 然后顾客线程1,9,3一次被调度。即在理发师睡觉期间进来了三个顾客。在第一个顾客进店时已经通过notify_one唤醒了理发师线程(就绪态)。在理发师醒来的同时,顾客9和3也进店了。
3. 理发师线程再次被调度,此时理发师线程变为执行态,理发师给顾客理发
4. 理发师理发期间,顾客6又进店了
5. 理发师已经服务完一名顾客再次调度,此时店内已经有顾客9,3,4,6。理发师继续选择下一个顾客进行服务。
6. 在理发师还在服务的时候,顾客7,8,2,10,11,12依次进店坐在椅子上等待理发。
7. 而顾客13,14,15,16,17,18,19,20,5进店之后发现没有多余的椅子了,顾客离开,不会等待直至有空闲椅子,因此这些顾客线程退出。此时只剩下9,3,4,6,7,8,2,10,11,12等顾客在店中,他们的线程也退出了,因为它们都在椅子上坐着,随着理发师线程的不断调度,它们总是会被服务的。
8. 此后没有任何顾客线程了,只有不断的理发师线程再调度,依次为各个顾客理发,直至没有任何顾客在椅子上等待。最后理发师服务完所有顾客又发现没有顾客了,理发师开始睡觉,线程阻塞。
输出结果如下:
没有顾客,理发师睡觉顾客 1 等待理发顾客 9 等待理发顾客 3 等待理发有顾客,理发师被唤醒顾客 4 等待理发理发师给顾客理发顾客 6 等待理发有顾客,理发师被唤醒顾客 7 等待理发顾客 8 等待理发顾客 2 等待理发顾客 10 等待理发顾客 11 等待理发顾客 12 等待理发顾客 13 没有位置,离开顾客 14 没有位置,离开顾客 15 没有位置,离开顾客 16 没有位置,离开顾客 17 没有位置,离开顾客 18 没有位置,离开顾客 19 没有位置,离开顾客 20 没有位置,离开顾客 5 没有位置,离开理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发没有顾客,理发师睡觉
- 进程同步之理发师问题
- 进程同步的经典问题3——理发师问题
- 操作系统(三)同步进程问题--理发师的睡觉问题
- 睡觉理发师问题——进程同步与死锁
- 操作系统 同步互斥问题之 理发师问题
- 进程(线程)间同步互斥问题(三) 熟睡的理发师问题
- 理发师问题
- 理发师问题
- 理发师问题
- 理发师问题
- 理发师问题
- 理发师问题
- 基于信号量采用多线程技术实现进程同步(贪睡的理发师)
- 睡眠理发师问题
- 睡眠理发师问题
- 睡眠理发师问题
- 信号量实现理发师问题
- 加强版理发师问题
- 酷狗音乐与genymotion端口冲突问题
- usaco2016open silver3 closing
- usaco2016open gold1 split
- Android坐标系统
- usaco2016open gold2 closing
- 进程同步之理发师问题
- usaco2016open gold3 248
- Activiti(十一)——分配组任务的三种方式
- gdoi2009中山市选T1 谁能赢呢?
- gdoi2009中山市选T2 小球
- USACO 2016 JANUARY CONTEST, BRONZE PROBLEM 1. PROMOTION COUNTING
- Android CTS Verifier bug - Streaming Video Quality Verifier
- USACO 2016 JANUARY CONTEST, BRONZE PROBLEM 3. MOWING THE FIELD(收割庄稼)
- STL之pair