进餐问题及其他信号量

哲学家问题

  五个哲学家围坐在一圆桌旁,桌中央有一盘通心粉,没人面前有一只空盘子,每两人之间放一只叉子。每个哲学家的行为是思考,感到饥饿,然后吃通心粉。为了吃通心粉,每个哲学家必须拿到两只叉子,并且每个人只能直接从自己的左边或有百年去取叉子

使用P、V操作解决

  每一只叉子用一个信号量表示,通过对信号量执行P操作取得叉子,执行V操作放下叉子

设置信号量:

semaphore  fork[n];//n=5,公用信号量,用于互斥
fork[i]=1;//设置初值,i=1,2,,, ,5
第i个哲学家进程
while(1){
    thingking,,,
    P(fork[i])//拿左手的叉子
    P(fork[i+1])//拿右手的叉子
    eating;
    V(fork[i+1])//放下右手的叉子
    V(fork[i]) //放下左手的叉子
    
    这样可以确保没有两个相邻的哲学家可以同时进餐
}

带来的问题:

  死锁--每个人拿起而且只拿起了一只叉子,并且都不放下,僵在这

  饥饿--每个人都拿起了叉子,然后动作很协调又同时放下,反复重复这个过程(长时间得不到响应)

原因:P、V操作本身可以很好的解决同步互斥问题,但是实际中遇到的问题并不都是同步互斥问题,并且P、V操作本身也存在着缺点:操作繁琐,同步互斥操作被分散在各个进程中,使得易读性、维护性差,复杂性高,对于一些复杂问题实现不方便,存在隐患。

如何解决?--提出了一些高级数据结构

信号量集--AND型信号量集

AND型信号量集是指同时需要多种资源且每种占用一个时的信号量操作

AND型信号量集的基本思想:在一个原语中申请整段代码需要的多个临界资源,要么全部分配给它,要么一个都不分配

AND型信号量集P原语为Swait

AND型信号量集V原语为Ssignal

Swait(S1,S2, ,,, ,Sn)//P原语
{
    while(TRUE){
        if(S1>=1&&S2>=1&& ,,, && Sn>=1){
            for(i=1,i<=n;++i){
                Si--;
            }    
            break;
        }
        else{//某些资源不够用的处理
            调用进程进入第一个小于1信号量的等待队列,阻塞调用进程;
        }
    }
}

Ssignal(S1, S2, …, Sn)
{
for (i = 1; i <= n; ++i)
  {
    ++Si;        //释放占用的资源;
    for (在Si.queue中等待的每一个进程P){    //检查每种资源的等待队列的所有进程;  
        从等待队列Si.queue中取出进程P;
        if (判断进程P是否通过Swait中的测试)
               //注:与signal不同,这里要进行重新判断;
        {//通过检查(资源够用)时的处理;
              进程P进入就绪队列;
          }
        else
         {//未通过检查(资源不够用)时的处理;
          进程P进入某等待队列;
          }
   }
 }
}

一般信号量集

一般信号量集是指同时需要多种资源、每种资源占用的数目不同、且可分配的资源还存在一个临界值时的信号量处理

一般信号量集的基本思路就是在AND型信号量集的基础上进行扩充,在一次原语操作中完成所有的资源申请

进程对信号量Si的

测试值为 ti (表示信号量的判断条件,要求Si≥ti,即当资源数量低于ti时,便不予分配)

占用值为di(表示资源的申请量,即Si=Si-di)

对应的P、V原语格式为:

Swait(S1,t1,d1;,,,;Sn,tn,dn);

Ssignal(s1,d1,;,,,;Sn,dn);

一般信号量可以用于各种情况的资源分配和释放,几种特殊情况:

Swait(S,d,d)表示每次申请d个资源,当少于d个时,便不分配)

Swait(S,1,1)表示互斥信号量

Swait(S,1,0)可作为一个可控开关(当S≥1时,允许多个进程进入临界区;当S=0时,禁止任何进程进入临界区)

原文地址:https://www.cnblogs.com/fate-/p/12668730.html