用管程解决生产者消费者问题

生产者消费者问题

系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。 生产者,消费者共享一个初始化为空,大小为n 的缓冲区。 如何使用信号量机制(P、V操作)实现生产者、消费者进程的这些功能呢? 信号量机制可实现互斥、同步、对一类系统资源的申请和释放。

对于信号量可以看这篇信号量机制

使用信号量解决生产者消费者问题

生产者、消费者共享一个初始为空、大小为n的缓冲区。只有缓冲区没满的时候,生产者才能把产品放入缓冲区,否则必须等待。只有缓冲区不为空的时候,消费者才能从中取出产品,否则必须等待。

缓冲区是临界资源,各个进程必须互斥访问。
首先定义信号量;

semaphore mutex=1; //互斥信号量,实现对缓冲区的互斥访问
semaphore empty=n; //同步信号量,表示空闲缓冲区的数量
semaphore full = 0; //同步信号量,表示产品数量,也是非空缓冲区的数量

producer(){
    while(1){
        生产一个产品;
        p(empty);  //小号一个空闲缓冲区
        p(mutex);  // 实现互斥是在同一进程中进行PV操作
        把产品放入缓冲区;
        V(mutex);
        V(full); //增加一个产品
    }

}
// 实现两个进程的同步,是在其中一个进程中执行P,另一个进程中执行V。

consumer(){
    while(1){
        P(full); //消耗一个产品
        P(mutex);
        从缓冲区取出一个产品
        V(mutex);
        V(empty);   //增加一个空闲缓冲区
        使用产品;
    
    }

}

管程

为啥要引入管程?

信号量机制存在问题:编写程序困难、易出错。能不能设计一种机制,让程序员写程序时不需要再关注复杂的PV操作,让写代码更加轻松呢?

1973年, Brinch Hansen首次在程序设计语言( Pascal中引入了“管程”成分一一一种高级同步机制)

管程的定义和基本特征

定义

管程是一种特殊的软件模块,由这些部分组成:

  1. 局部于管程的共享数据结构说明;
  2. 对该数据结构进行操作的一组过程(函数)
  3. 对局部于管程的共享数据设置初始值的语句
  4. 管程自己的名字

特征

  1. 管程内的数据只能被管程中定义的函数(过程)访问;(类比于java实体类的get方法)
  2. 一个进程只能通过调用管程内的过称(函数/方法)才能进入管程访问共享数据(进程.getXxx())
  3. 每次仅仅允许一个进程在管程内自行某个内部的过程

用管程解决生产者消费者问题

定义一个管程以下是伪代码

monitor ProducterConsumer
    condition full,empty;  //条件变量用来实现同步(排队)
    int count=0; // 缓冲区中的产品数
    void insert(Item item){
    //把产品item放入缓冲区
    if(count == N)
        wait(full);
    count++;
    insert_item(item);
    if(count == 1)
        signal(empty);
    
    }
Item remove(){ //从缓冲区取出一个产品
    if(count==0)
        wait(empty);
    count--;
    if(count == N-1)
        signal(full);
    return remove_item();
}
end monitor;

生产者进程

producter(){
    while(1){
        item = 生产一个产品;
        ProducterConsumer.insert(item);
       }
}

消费者进程

consumer(){
    while(1){
        item = ProducterConsumer.remove();
        消费产品;
    
    }
}

引入管程的目的无非就是更方便地实现进程的互斥与同步

  1. 需要在管程中定义共享数据
  2. 需要在管程中定义用于访问这些共享数据的“入口”–其实就是一些函数
  3. 只有通过这些函数才能访问管程中的共享数据
  4. 管程中有很多接口,但是每次只能开放其中一个入口,并且只能让一个进程或线程进入,(互斥的特性交给了编译器去实现,程序员不用关心)
  5. 可在管程中设置条件变量级等待/唤醒操作以解决同步问题。

通过管程提供特定的接口来实现进程的同步与互斥,这就是一种封装的思想。

猜你喜欢

转载自blog.csdn.net/qq_43672652/article/details/107677592