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


    生产者消费者问题

    系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。 生产者,消费者共享一个初始化为空,大小为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. 可在管程中设置条件变量级等待/唤醒操作以解决同步问题。

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

  • 相关阅读:
    [工具推荐]005.Axure RP Pro 7.0模拟C#TAB控件
    [安卓基础] 008.Android中的显示单位
    [JavaWeb基础] 008.Spring初步配置
    [批处理教程之Shell]002.Linux 常用命令大全
    [注]新手学习编程的最佳方式是什么?
    [C#打包部署教程]001.VS2012 + InstallShield
    [站点推荐]001.学习新技能的37个最佳网站(The 37 Best Websites To Learn Something New)
    程序员如何像写代码一样找女朋友
    [工具-006] C#如何模拟发包登录
    [Linux系统] (1)常用操作(CentOS 7.x)
  • 原文地址:https://www.cnblogs.com/itjiangpo/p/14181346.html
Copyright © 2020-2023  润新知