进程同步问题(三)——典型实例

以下介绍几个几个经典的进程同步的问题。

一、生产者-消费者模型问题

  1. 模型描述:一组生产者进程和一组消费者进程共享一个初始为空、大小为n的缓冲区,只有缓冲区没满时,生产者才把消息放入缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或一个消费者从中取出消息。
  2. 分析:
  • 进程关系:生产者和消费者对缓冲区互斥访问是互斥关系,同时生产者和消费者是相互协作的,并且只有在生产者生产之后,消费者才能消费,他们也是同步关系。
  • 就只有两个进程,存在互斥与同步的关系,需要考虑互斥和同步的PV操作的设置。
  • 信号量:信号量mutex作为互斥信号量,用于控制互斥访问缓冲区,互斥信号量初始值为1;信号量full用于记录当前缓冲区装填的生产者生成的资源数量,初始值为0.信号量empty用来记录当前缓冲区剩余大小,初始值为缓冲区总容量n。
  • 模型描述如下:
semaphore mutex = 1;  //临界区互斥信号量
semaphore empty = n;  //空闲缓冲区
semaphore full = 0;   //缓冲区初始化为空

Producer(){
    while(true){
        生产一个资源;
        P(empty);  //获取空缓冲区单元,empty--
        P(mutex);  //互斥访问临界区
        把资源放入缓冲区;
        V(mutex)   //释放mutex,退出临界区
        V(full)    //full++
    }
}

Consumer(){
    while(true){
        P(full); //full--
        P(mutex);//互斥访问临界区
        从缓冲区取出数据)();
        V(mutex); //释放mutex,退出临界区
        V(empty); //empty++
        把数据拿去使用();
    }
}
  • 解析:该类问题要注意对缓冲区大小为n的处理,当缓冲区有空时,便可对empty变量进行P操作,一旦取走一个资源便要执行V释放一个缓冲区空间。此外,对empty和full的P操作必须放在对mutex的P操作之前。若生产者先P(mutex),然后执行P(empty);消费者先执行P(mutex),再执行P(full),是不可取的。因为假设缓冲区已经满,生产者继续运行,先执行P(mutex),然后P(empty)时将被阻塞,然后若消费者想从缓冲区取资源,先P(mutex),这时消费者也被阻塞,这样二者无法再继续进行下去了!!!如果消费者进程将缓冲区资源拿完,然后紧接着消费者继续按以上顺序运行,也会造成二者均阻塞的情况。在释放信号量V操作时,先后释放都可以。

二、读者-写者问题

  1. 有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时没有问题,但是如果某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:①允许多个读者可以同时对文件执行读操作;②只允许一个写者往文件中写信息;③任一写者在完成写操作之前不允许其他读者或写者工作;④写者执行写操作前,应该在满足已有的读者和写者全部退出。
  2. 问题分析:
  • 进程关系的分析:读者和写者是互斥的,写者和写者也是互斥的,而读者和读者不存在互斥。
  • 两个进程,即读者和写者。写者与任何进程互斥,用互斥信号量的P操作、V操作即可解决。读者必须实现与写者互斥的同时,实现与其他读者同步,所以用简单的一对P、V操作是无法解决问题的。在这里用到了一个计数器,用它来判断当前是否有读者文件。当有读者时,写者是无法写文件的,此时读者会一直占用文件,当没有读者时,写者才可以写文件。同时,这里不同的读者对计数器的访问也应该是互斥的。
  • 信号量设置:首先设置信号量count为计数器,用于记录当前读者的数量,初值为0;设置mutex为互斥信号量,用于保护更新count变量时的互斥;设置互斥信号量rw,用于保证读者和写者的互斥访问。
  • 代码如下:
int count = 0;        //用于记录当前的读者数量
semaphore mutex = 1;  //用于保护更新count变量时的互斥
semaphore rw = 1;     //用于保证读者和写者互斥地访问文件

Writer(){
    while(true){
        P(rw);    //互斥访问共享文件
        写入;     //写入
        V(rw);    //释放共享文件
    }
}

Reader(){
    while(true){
        P(mutex);      //互斥访问count变量
        if(count == 0) //当第一个读进程 读共享文件时
            P(rw);     //阻止写进程写
        count++;       //读者计数器加一
        V(mutex);      //释放互斥变量
        读取;          //
        P(mutex);
        count--;       //读者计数器减一
        if(count == 0) //当最后一个读进程读完共享文件
            V(rw);     //允许写进程写
        V(mutex);      //释放互斥变量count
    }
}
  • 在以上算法,读进程是优先的,即当存在读进程时,写操作将被延迟,且只要有一个读进程活跃,随后而来的读进程都将被允许访问文件。这样的方式会导致写进程可能长时间等待,且存在写进程“饿死”的情况。
  • 若希望写进程优先,即当有读进程正在读共享文件,有写进程请求访问,这时应该禁止后续读进程的请求,等到已在共享文件的读进程执行完毕,立即让写进程执行,只有在无写进程执行的情况下才允许读进程再次运行。为此,增加一个信号量并在上面程序的Writer()和Reader()中各增加一对PV操作,就可以得到写进程优先的解决程序
int count = 0;        //用于记录当前的读者数量
semaphore mutex = 1;  //用于保护更新count变量时的互斥
semaphore rw = 1;     //用于保证读者和写者互斥地访问文件
semaphore w = 1;      //用于实现"写优先"

Writer(){
    while(true){
        P(w);     //在无写进程请求时进入
        P(rw);    //互斥访问共享文件
        写入;     //写入
        V(rw);    //释放共享文件
        V(w);     //恢复对共享文件的访问
    }
}

Reader(){
    while(true){
        P(w);          //在无写进程请求时进入
        P(mutex);      //互斥访问count变量
        if(count == 0) //当第一个读进程 读共享文件时
            P(rw);     //阻止写进程写
        count++;       //读者计数器加一
        V(mutex);      //释放互斥变量
        V(w);          //恢复对共享文件的访问
        读取;          
        P(mutex);      
        count--;       //读者计数器减一
        if(count == 0) //当最后一个读进程读完共享文件
            V(rw);     //允许写进程写
        V(mutex);      //释放互斥变量count
    }
}
  • 这种方法又称为“读写公平法”,即读写进程具有一样的优先级。
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值