生产者消费者问题 操作系统

2.15 生产者消费者问题

Dijkstra对同步问题的抽象

抽象解释

  • 一个或多个生产者生产资源,放入临界区
  • 一次仅有一个消费者访问临界区并消耗资源
  • 每次仅有一名生产者/消费者访问临界区
  • 临界区满时生产者不会继续添加资源
  • 临界区空时消费者不会继续消耗资源

流程图

要使用一个空间信号量告知生产者有无空间
要使用一个资源信号量告知消费者有无资源
在这里插入图片描述
在这里插入图片描述

2.15.1 生产者消费者问题要点

  • 先申请资源,再申请互斥,顺序不可颠倒,否则假设消费者P0申请了互斥后,等待资源,生产者P1需要生产资源,却无法申请互斥进入临界区,形成死锁
  • 同一个进程中的空间、资源semSignal语句可以颠倒,因为先释放互斥和先释放资源没有影响,另一个进程都需要在两个信号量上等待,但是可能会降低性能,所以一般先释放资源再释放互斥
  • 同一个信号量的wait和signal可能在不同进程,如资源信号量,也可能在同一进程,如互斥信号量,但必须成对
  • 对于互斥信号量,wait一定先于signal,资源信号量则有可能先signal

在这里插入图片描述

2.15.2 生产者消费者问题示例


示例1
情境描述
  • 桌子上有一只盘子,最多能放N(N>0)个水果(资源空间N)
  • 爸爸随机向盘子中放入苹果或橘子(一个生产者)
  • 儿子只吃橘子,女儿只吃苹果(两个消费者)
  • 没有自己想吃的水果就不吃(有各自资源信号量时消费者可以消耗)
  • 盘子满了爸爸就不放水果(空间信号量有限)
  • 每次只能放入或取出一个水果,不允许同时使用盘子(互斥使用临界区)

用信号量机制实现爸爸、儿子和女儿之间的同步与互斥活动,并说明所定义信号量的含义。要求用伪代码描述。

分析
  • 一个生产者,针对两个消费者生产两种不同资源,共用一个大小为N的临界区
  • 盘子是临界区,需要互斥信号量mutex
  • 爸爸和儿子因为orange的产生和消耗而同步:先产生后消耗
  • 爸爸和女儿因为apple的产生和消耗而同步:先产生后消耗
  • 爸爸和儿子、女儿共享临界区,需要空间信号量empty
  • 信号量:
  • empty:盘子是否为空
  • orange:有无资源orange
  • apple:有无资源apple
  • mutex:互斥信号量
伪代码实现
semaphore mutex = 1;                       //盘子操作互斥信号量
semaphore apple = 0, orange = 0;    //苹果、桔子放入、取出的资源信号量
semaphore empty = N;                      //盘子中可放入的水果数目

P1()
{
    
    
    while (true) {
    
    
         result= prepare _fruit();    //准备水果,result为水果类型
         P(empty);                             //盘子中可放入的水果数目减1
         P(mutex);                             //互斥访问盘子
         put a fruit on the plate;      //将一个水果放入盘子
         V(mutex);                            //恢复访问盘子
         if (result == apple)              //准备的水果为苹果
             V(apple);                          //允许女儿取苹果
         else                                        //准备的水果为桔子
             V(orange);                        //允许儿子取桔子
    }
}
son() {
    
    
    while (true) {
    
    
         P(orange);                             //判断是否可取桔子
         P(mutex);                             //互斥访问盘子
         get an orage from plate();  //取桔子
         V(mutex);                             //恢复访问盘子
         V(empty);                             //盘子中可放入的水果数目加1
    }
}

daughter() {
    
    
    while (true) {
    
    
         P(apple);                               //判断是否可取苹果
         P(mutex);                             //互斥访问盘子
         get an apple from plate();   //取苹果
         V(mutex);                             //恢复访问盘子
         V(empty);                             //盘子中可放入的水果数目加1
    }
}


示例2
情境描述
  • 桌子上有一只盘子,只能放一个水果(资源空间1)
  • 爸爸向盘子中放入苹果,妈妈向盘子中放入橘子(两个生产者)
  • 儿子只吃橘子,女儿只吃苹果(两个消费者)
  • 没有自己想吃的水果就不吃(有各自资源信号量时消费者可以消耗)
  • 只有盘子为空时爸爸或妈妈才能放入水果(空间信号量有限)
  • 每次只能放入或取出一个水果,不允许同时使用盘子(互斥使用临界区)

用信号量机制实现爸爸、妈妈、儿子和女儿之间的同步与互斥活动,并说明所定义信号量的含义。要求用伪代码描述。

分析
  • 两个生产者,针对两个消费者生产两种不同资源,共用一个大小为1的临界区
  • 盘子是临界区,需要资源信号量plate
  • 爸爸和女儿因为apple的产生和消耗而同步:先产生后消耗
  • 妈妈和儿子因为orange的产生和消耗而同步:先产生后消耗
  • 信号量:
  • plate:盘子是否为空
  • orange:有无资源orange
  • apple:有无资源apple

#####为什么不需要互斥信号量
因为不需要

  • 在示例一中,空间可以容纳多个资源,假设空间为5,当现存3个资源时,生产者和消费者可能同时访问临界区,一个是去放新的资源,一个是去取旧的资源,这时需要空间和互斥两个信号量
  • 而在示例二中现存0个资源时只有生产者可以访问临界区,现存1个资源时只有消费者可以访问临界区,所以在生产者进入临界区,还未使得资源信号量++时,消费者不是因为互斥无法进入,而是因为没有资源可取,只有当生产者出临界区后才会请求进入临界区取新生产的资源,消费者进入临界区时同理,生产者也不会进入临界区
  • 所以不需要互斥
伪代码实现
semaphore plate = 1;                         //是否允许向盘子放入水果
semaphore apple = 0, orange = 0;    //盘子中是否有苹果、桔子
dad() {
    
    
    while (true) {
    
    
         prepare an apple;
         P(plate);                               //互斥向盘子放水果
         put an apple on the plate;  //将苹果放入盘中
         V(apple);                             //允许取苹果
    }
}
mom() {
    
    
    while (true) {
    
    
         prepare an orange;
         P(plate);                                 //互斥向盘子放水果
         put an orange on the plate;  //将桔子放入盘中
         V(orange);                              //允许取桔子
    }
}
son()
{
    
    
    while (true) {
    
    
         P(orange);                                    //互斥取水果
         get an orange from the plate;     //从盘中取出桔子
         V(plate);                                       //允许向盘中放入水果
    }
}

daughter()
{
    
    
    while (true) {
    
    
         P(apple);                                      //互斥取水果
         get an apple from the plate;       //从盘中取出苹果
         V(plate);                                       //允许向盘中放入水果
    }
}


示例3
情境描述
  • 桌子上有一只盘子,最多放两个水果(资源空间2)
  • 爸爸向盘子中放入苹果,妈妈向盘子中放入橘子(两个生产者)
  • 女儿负责吃水果(一个消费者)
  • 当且仅当盘子同时存在苹果和橘子时女儿才会取出水果(有资源信号量时消费者可以消耗)
  • 只有盘子为空时爸爸或妈妈才能放入水果(空间信号量有限)
  • 每次只能放入或取出一个水果,不允许同时使用盘子(互斥使用临界区)

用信号量机制实现爸爸、妈妈、女儿之间的同步与互斥活动,并说明所定义信号量的含义。要求用伪代码描述。

分析
  • 两个生产者,生产两种不同资源,共用一个大小为2的临界区
  • 爸爸和女儿因为apple的产生和消耗而同步:先产生后消耗
  • 妈妈和儿子因为orange的产生和消耗而同步:先产生后消耗
  • 盘子是否可以放入苹果需要判断:apple_empty
  • 盘子是否可以放入橘子需要判断:orange_empty
  • 信号量:
  • apple_empty:是否可以放apple
  • orange_empty:是否可以放orange
  • orange:有无资源orange
  • apple:有无资源apple

#####为什么不需要互斥信号量
因为不需要

  • 在示例一中,空间可以容纳多个资源,假设空间为5,当现存3个资源时,生产者和消费者可能同时访问临界区,在示例二中现存0个资源时只有生产者可以访问临界区,现存1个资源时只有消费者可以访问临界区,所以在生产者进入临界区,还未使得资源信号量++时,消费者不是因为互斥无法进入,而是因为没有资源可取,只有当生产者出临界区后才会请求进入临界区取新生产的资源
  • 而在示例三中看似是一个空间,实际上是两个独立的大小为1的空间,因为大小为2的空间却只能放1个苹果和一个橘子,不能放两个相同的水果,就相当于两个盘子,每个盘子只能放一种水果,两个盘子的水果不能相同,参考示例二,这种情况也不需要互斥
伪代码实现
semaphore apple = 0, orange = 0;    //盘子中是否有苹果、桔子
semaphore empty_apple = 1, empty_orange = 1;    //盘子是否可放入苹果、桔子

dad(){
    
    
    while (true) {
    
    
         prepare an apple;
         P(empty_apple);                     //盘子中是否可放入苹果
         put an apple on the plate;      //将一个苹果放入盘子
         V(apple);                                  //允许女儿取苹果
    }
}
mom(){
    
    
    while (true) {
    
    
         prepare an orange;
         P(empty_orange);                    //盘子中是否可放入桔子
         put an orange on the plate;      //将一个桔子放入盘子
         V(orange);                                  //允许女儿取桔子
    }
}
daughter() {
    
    
    while (true) {
    
    
         P(apple);                               //盘子中是否有苹果
         P(orange);                              //盘子中是否有桔子
         get an apple  and an orange from plate();   //取水果
         V(empty_apple);                          //盘子中可以放入苹果
         V(empty_orange);                         //盘子中可以放入桔子
    }
}


示例4
情境描述
  • 女儿负责画画(一个生产者)
  • 爸爸、妈妈负责欣赏(两个消费者)
  • 女儿在白板上画完一幅画后,请爸爸、妈妈均欣赏过一遍后,再创作新画
  • 一个生产者进程和两个消费者进程共享大小为1的缓冲,当且仅当缓冲为空时,生产者进程负责放入数据,当且仅当缓冲有数据时,消费者读数据,只有当两个消费者都读取数据后,生产者才能删除原有数据并继续生产下一个数据。

用信号量机制实现爸爸、妈妈、女儿之间的同步与互斥活动,并说明所定义信号量的含义。要求用伪代码描述。

分析
  • 一个生产者和两个消费者,一幅画要分别给两人看
  • 可看作一个生产者每次要针对不同消费者生产两个数据
  • 两个数据都被消费后才能继续生产
  • 爸爸是否欣赏过,设置空间资源信号量empty_dad
  • 爸爸是否可以欣赏,设置产品资源信号量full_dad
  • 妈妈是否欣赏过,设置空间资源信号量empty_mom
  • 妈妈是否可以欣赏,设置产品资源信号量full_mom
  • 信号量:
  • empty_dad:爸爸是否欣赏过
  • empty_mom:妈妈是否欣赏过
  • full_dad:爸爸是否可以欣赏
  • full_mom:妈妈是否可以欣赏
伪代码实现
semaphore empty_dad = 1, empty_mom = 1;  //爸爸、妈妈是否已看过女画的新画
semaphore full_dad = 0, full_mom = 0;           //是否存在可供爸爸、妈妈看的新画

daughter(){
    
    
    while (true) {
    
    
         P(empty_dad);                                    //爸爸是否看过
         P(empty_mom);                                    //妈妈是否看过
         draw a new picture on the whiteboard;            //画一幅新画
         V(full_dad);                                     //爸爸可以看了
         V(full_mom);                                     //妈妈可以看了
    }
}
dad() {
    
    
    while (true) {
    
    
         P(full_dad);                                  //白板上是否存在没有看过的画
         enjoy the picture on the whiteboard;          //看画
         V(empty_dad);                                 //爸爸已看过新画
    }
}

mom() {
    
    
    while (true) {
    
    
         P(full_mom);                                  //白板上是否存在没有看过的画
         enjoy the picture on the whiteboard;          //看画
         V(empty_mom);                                 //妈妈已看过新画
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44722674/article/details/111569955