关于进程同步问题(P、V操作)之解题思路的一点意见

在学习到这一块内容的时候,尤其是关于信号量机制(P、V操作)的内容时,我总感觉缺少一种可称呼为algorithm的东西将这些操作以适当的方式组织起来。面对一些经典问题时我们或许可以靠死记硬背来解答,但这显然不适合处理那些“新颖的题型”。中文网络中我也没看到有什么合适的方案。鉴于此,经本人研究,我感觉自己构想出了合适的步骤来回答这类问题。

处理P、V操作最大的困难在于交互性(intersubjectivity)的分析。 一般情况下,我们写代码时考虑所关注的对象有何种属性;但对于进程同步问题而言,进程本身的属性是不重要的,它与哪些资源产生关联才是重要的——这些关联正是信号量机制所调节的对象。

举个例子。有一个经典的生产者-消费者问题是说,桌上有一个最多只能放1个水果的盘子。爸爸专门往盘子里放苹果,妈妈专门往盘子里放橘子;儿子专吃盘子里的橘子,女儿专吃盘子里的苹果。盘子为空时才能放水果,盘子里有水果时、水果才能被取走。请用信号量机制描述四人的进程。

这个问题涉及的对象包括4个人和1个盘子,各对象之间的关联可以用以下方式来描绘:

其中,带箭头的线就代表一种关系。我们有理由断言:凡是存在a->b的关系(如father->plate),那么必有b->a的关系(如plate->father)。这是因为a对b所做的动作不是无限制的,它必然受到b对a的反馈的调节。(在这个例子中,就是plate的容量制约了father放水果的动作)

在a与b的交互中,如果我们站在a的视角,那么可以把a->b的关系定义为一种主动关系,而b->a则定义为被动关系。我们可假定这种关系是可传递的,即(站在a的视角)a->b与b->c可以推出a->c,且a->c是a的主动关系;类似的,b->a和c->b可推出c->a,且c->a是a的被动关系。(不否认自反性的可能,但这要根据实际情形做斟酌)

由于关系a->b与b->a总是相互受到制约(至少在进程同步问题里总是如此),我们可以为这一对关系赋予相同的名称。不同对的关系的名称可能相同也可能不同,取决于双方之间的数量变化。名称可以是毫无意义的。如下图所示。

结合题设,当father对plate发出主动关系active(f)时,女儿一方就能得到一个被动关系passive(d),同时mother将无法发出主动关系active(m)。反之,mother发出active(m)时,儿子获得了passive(s),而父亲此时无法发出active(f)。当女儿发出active(d),或儿子发出active(s)时,父亲和母亲均能获得被动关系active(f)、active(m),使得他们再一次发出主动关系得以成为现实可能。因此f和m是同一对关系,即f=m。上图可以被简化为以下。

直觉敏锐的读者此时就能发现,p、d、s其实就是本例需要设置的三个信号,分别指向盘子的容量、苹果的数量和橘子的数量。

信号的值如何确定?只要考察在最开始时、各方能够发出的主动关系的数量即可。在最开始,父亲或母亲都可以发出动作active(p),而女儿不能发出active(d)、儿子不能active(s)。(按题设,盘子一开始是空的)所以p的初始值为1,d和s的初始值均为0 。

既然信号的设定已经实现,那么如何编入到进程之中呢?这里不妨定义一个名叫“视域”的概念。所谓“视域”就是某一对象的全部主动关系的一个子集,这个子集中任意一个关系的目标(或者也可以说是“客体”)都是该对象(也可以说是“主体”)所要影响的目标,也即主体欲施加于变化的客体;而该子集之外不存在能满足上述条件的关系。借助这个具有强烈直观色彩的概念,我们可以说四个进程(即father,mother,daughter,son)所涉及的信号量都涉及且只涉及视域内的主动关系。如下图所示,我用不同颜色来标识视域的不同。

这样就能敲定最终答案了,如以下所示。聪明的读者应该很快就能发现,主动关系意味着信号量机制里的P,而被动关系意味着信号量机制里的V 。

semaphore p=1,d=0,s=0;

cobegin
{
    Process father(){
        while(1){
            //prepare an apple
            P(p);
            //put the apple on the plate
            V(d);
        }
    }
    
    Process mother(){
        while(1){
            //prepare an orange
            P(p);
            //put the orange on the plate
            V(s);
        }
    }
    
    Process son(){
        while(1){
            P(s);
            //take the orange from the plate
            V(p);
            //eat the orange
        }
    }
    
    Process daughter(){
        while(1){
            P(d);
            //take the apple from the plate
            V(p);
            //eat the apple
        }
    }
}
coend

如果你对上述方法有了一定的了解,那么可以用其它例题来强化理解。这里选择经典的读者-写者问题。则此类图示可以表示为如下,这是一个读者优先的解决方案。

在本图中,我们认为“主体”就是每一个writer和reader。由此可以得到敲定以下答案。

int count=0;
semaphore rw_mutex=1,ct_mutex=1;
cobegin
{
    Process writer(){
        P(rw_mutex);
        //Writing
        V(rw_mutex);
    }
    
    Process reader(){
        P(ct_mutex);
        if(count==0) P(rw_mutex);
        count++;
        V(ct_mutex);
        //Reading
        P(ct_mutex);
        count--;
        if(count==0) V(rw_mutex);
        V(ct_mutex);
    }
}
coend

如果希望每个进程都能公平地接触到文件,那么只要按下图方式调整交互关系即可,代码略。需要注意的是active(p_mutex)在读进程手中不能构成任何制约(即在reading之前就实施passive(p_mutex)),但在写进程手中可以锁定当前访问文件的进程数量(即完成写操作后再实施passive(p_mutex))。

我们可以看到,对被动关系的设定要比对主动关系的设定往往有更大的自由度,尽管这并不意味着完全不考虑顺序。一个主体对于其所有的主动关系,他可以借助于传递性而“看”到对应的被动关系;也可以对其一无所知。因此我们的作图必须要非常审慎,如此才能不产生任何误导。一般来说,如果客体是一个不受人控制的“单独物体”,那么主体对其发出主动关系时,他大约可以看到被动关系;反之,基本上就看不到了。

我认为本方法作为一种辅助手段,然能极大地对题目分析产生助益。尽管如此,若使用不当,还是很容易会造成错误的。

以2011年某统考真题为例。某银行提供1个服务窗口和10个供顾客等待的座位。顾客到达银行时,若有空座位,则在取号机上取号,并于座位上等待叫号。取号机同时只能被1名顾客操作。当营业员空闲时,则通过叫号选取1为顾客,并为其服务。

对于顾客(customer)和营业员(clerk)两个进程,我们可以画出以下图示。

答案如下。

semaphore a=10, b=1, c=0, d=0;

cobegin{
    Process Customer(){
        P(a);
        //While there's a seat
        P(b);
        V(b);
        V(c);
        P(d);
        //being serviced
    }
    
    Process Clerk(){
        while(1){
            P(c);
            V(a);
            V(d);
        }
    }
}
coend

由于完全采用有向图的方式来考察各种交互行为的数量关系,因此“意义”就被屏蔽了。读者们从以上例子中可以发现,信号的名字对于我们分析问题而言已经变得无关紧要,或者说甚至就是一个累赘。尽管如此,为了使图示契合题意,信号的现实意义也是需要考虑的。

不难发现同一个题目可以给出多个图示,从而得出不同的答案。这些答案中有的简洁,有的则繁琐。不管美观上如何,个人认为能把题目做出来才是最关键的。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值