进程同步之理发师问题

来源:互联网 发布:岸谷新罗知乎 编辑:程序博客网 时间: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 没有位置,离开理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发有顾客,理发师被唤醒理发师给顾客理发没有顾客,理发师睡觉
1 0
原创粉丝点击