经典同步问题

文章详细介绍了如何使用PV操作解决并发控制中的经典问题,包括生产者-消费者问题、多生产者-多消费者问题、吸烟者问题以及读者-学者问题。通过设置和使用信号量,确保了进程间的互斥访问和同步关系,防止死锁并实现资源的有效利用。
摘要由CSDN通过智能技术生成

同步问题是一个复杂的问题,但是它也有自己的方法去处理、去分析。

PV操作系统的解题思路:

  1. 关系分析。找出题目中描述的各个进程,分析它们之间的同步、互斥关系。(从事件的角度分析)

  1. 整理思路。根据各进程的操作流程确定P、V操作的大致顺序。

  1. 设置信号量。设置需要的信号量,并根据题目条件确定信号量的初值。(互斥信号量初值一般为1,同步信号量的初始值要看对应资源的初始值是多少)

1.生产者-消费者问题

问题描述:系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓存区中取出一个产品并使用。(注:这里的"产品"理解为某种数据)

分析:

生产者、消费者共享一个初始为空、大小为n的缓冲区

只有缓冲区没满时,生产者才能把产品放到缓冲区,否则必须等待。(缓冲区没满->生产者生产)

只有缓冲区不空时,消费者才能从中取出产品,否则必须等待。(缓冲区没空->消费者消费)

缓冲区是临界资源,各进程必须互斥的访问

根据分析,两个同步,一个互斥,所以需要3个信号量。定义如下:

semaphore mutex=1;//互斥信号量,实现对缓冲区的互斥访问
semaphore empty=n;//同步信号量,表示空闲缓冲区的数量
semaphore full=0;//同步信号量,表示产品的数量,也即非空缓冲区的数量
//生产者
Producer(){
    while(1){
          生产一个产品;
          P(mutex);
          P(empty);
          把产品放入缓冲区;
          V(mutex);
          V(full);
   }
}
//消费者
Consumer(){
     while(1){
     P(mutex);
     P(full);
     从缓冲区中取走一个产品;
     V(mutex);
     V(empty);
     使用产品;
}
}

注意实现互斥的P操作一定要在实现同步的P操作之后。否则会造成死锁的现象。(V操作不会导致进程阻塞,因此两个V操作顺序可以交换)。

2.多生产者-多消费者问题

问题描述:桌子上有一个盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子装等着吃盘子中的橘子,女儿装等着吃盘子中的苹果。只有当盘子空时,爸爸或妈妈才能向盘子中放一个水果。仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出水果。

分析:

互斥关系:对缓冲区(盘子)的访问要互斥地进行

同步关系(一前一后):

  1. 父亲将苹果放入盘子后,女儿才能取苹果。

  1. 母亲将橘子放入盘子后,儿子才能取橘子。

  1. 只有当盘子为空时,父亲或母亲才能放入水果。

根据分析,三个同步,一个互斥,所以需要个信号量。定义如下:

semaphore mutex=1;//实现互斥访问盘子(缓冲区)
semaphore apple=0;//盘子中有几个苹果  
semaphore orange=0;//盘子中有几个橘子
semaphore plate=1;//盘子中还可以放多少个水果

生产者、消费者定义如下:

Dad(){
  while(1){
     准备一个苹果;
     P(plate);
     P(mutex);
     把苹果放入盘子;
     V(mutex);
     V(apple);
  }
}
Mom(){
  while(1){
     准备一个橘子;
     P(plate);
     P(mutex);
     把橘子放入盘子;
     V(mutex);
     V(orange);
  }
Daughter(){
  while(1){
     P(apple);
     P(mutex);
     从盘子中取出苹果;
     V(mutex);
     V(palte);
     吃掉苹果;
  }
Son(){
  while(1){
     P(orange);
     P(mutex);
     从盘子中取出橘子;
     V(mutex);
     V(palte);
     吃掉橘子;
  }

注意:在本例子当中,如果不设置互斥信号量也是可以实现互斥的,原因是缓冲区大小是1,在任何时刻,三个同步信号量只有一个为1。

3.吸烟者问题

问题描述:假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但是要卷起并抽掉一支烟,抽烟者需要三种材料:烟草、纸、胶水。三个抽烟者中,第一个拥有烟草、第二个拥有纸、第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料在桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)

分析:

组合一:纸和胶水

组合二:烟草和胶水

组合三:烟草和纸

同步关系(从事件的角度分析):

  1. 桌子上有组合一->第一个抽烟者取走东西

  1. 桌子上有组合二->第二个抽烟者取走东西

  1. 桌子上有组合三->第三个抽烟者取走东西

  1. 发出完成信号->供应者将下一个组合放到桌上

和上题一样,本例子缓冲区大小为1,4个同步不信号量中同一时刻至多有一个值为1,所以不需要互斥信号量,定义如下:

semaphore offer1=0;//桌子上组合一的数量
semaphore offer2=0;//桌子上组合二的数量
semaphore offer3=0;//桌子上组合三的数量
semaphore finish=0;//抽烟是否完成
int i=0;//用于实现"三个抽烟者轮流抽烟"

供应者和抽烟者定义如下:

Provider(){
  while(1){
      if(i==0){
         将组合一放桌上;
          V(offer1);
          }
       else if(i==0){
         将组合二放桌上;
          V(offer2);
          }
       else if(i==0){
         将组合三放桌上;
          V(offer3);
          }
        i=(i+1)%3;
        P(finish)
  }
}
Smoker1(){
   while(1){
     P(offer1);
     从桌上拿走组合一、卷烟、抽掉;
     V(finish);
    }
}
Smoker2(){
   while(1){
     P(offer2);
     从桌上拿走组合二、卷烟、抽掉;
     V(finish);
    }
}
Smoker3(){
   while(1){
     P(offer3);
     从桌上拿走组合三、卷烟、抽掉;
     V(finish);
    }
}

4.读者-学者问题

问题描述:有读者写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:①允许多个读者可以同时对文件执行读操作②只允许一个写着往文件中写信息③任一写者在完成写操作之前不允许其他读者或写着工作④写者执行写操作,应让已有的读者和写者全部退出。

分析:

两类进程:写进程、读进程

互斥关系:写进程-写进程、写进程-读进程。读进程与读进程不存在互斥问题。

实现1:

semaphore rw=1;  //用于实现对共享文件的互斥访问
int count =0;  //记录当前有几个读进程在访问文件
semaphore mutex=1;  //用于保证对count变量的互斥访问

写进程

writer() {
   while(1){
     P(rw); //写之前"加锁"
     写文件...
    V(rw);  //写完了"解锁'
  }
 }

读进程

reader(){
while(1){
  P(mutex); //各读进程要互斥访问count
  if(count==0) //由第一个读进程负责
      P(rw); //读之前"加锁"
   count++; //访问文件的读进程数+1
   V(mutex);
   读文件...
   P(mutex);//各读进程要互斥访问count
   count--; //访问文件的读进程数+1
   if(count==0) //由最后一个读进程负责
      V(rw); //读完了"解锁"
    V(mutex);
 }
}

这个实现方法有个潜在的问题:只要有读进程还在读,写进程就要一直阻塞等待,可能"饿死"。因此,这种算法中,读进程是优先的。

还有一个问题思考一下:若两个读进程并发执行,则count=0时两个进程也许都能满足if条件,都会执行P(rw),从而使第二个读进程阻塞的情况。如何解决:出现上述问题的原因在于对count变量的检查和赋值无法一气呵成,因此可以设置另一个互斥信号量来保证各读进程对count的访问时是互斥的。这就引入了下面得到实现方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大磊程序员(“hello world”)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值