管程

管程               
信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。
本节将从以下几个方面进行介绍--

一. 管程的概念

二. 实例

一. 管程的概念
1. 管程的形式

管程作为一个模块,它的类型定义如下:
    monitor_name = MoNITOR;
       共享变量说明;
       define 本管程内部定义、外部可调用的函数名表;
       use 本管程外部定义、内部可调用的函数名表;
       内部定义的函数说明和函数体
       {
         共享变量初始化语句;
       }

 


2. 管程的特点

从语言的角度看,管程主要有以下特性:
  (1)模块化。管程是一个基本程序单位,可以单独编译;
  (2)抽象数据类型。管程是中不仅有数据,而且有对数据的操作;
  (3)信息掩蔽。管程外可以调用管程内部定义的一些函数,但函数的具体实现外部不可见;
对于管程中定义的共享变量的所有操作都局限在管程中,外部只能通过调用管程的某些函数来间接访问这些变量。因此管程有很好的封装性。
为了保证共享变量的数据一致性,管程应互斥使用。 管程通常是用于管理资源的,因此管程中有进程等待队列和相应的等待和唤醒操作。在管程入口有一个等待队列,称为入口等待队列。当一个已进入管程的进程等待时,就释放管程的互斥使用权;当已进入管程的一个进程唤醒另一个进程时,两者必须有一个退出或停止使用管程。在管程内部,由于执行唤醒操作,可能存在多个等待进程(等待使用管程),称为紧急等待队列,它的优先级高于入口等待队列。
因此,一个进程进入管程之前要先申请,一般由管程提供一个enter过程;离开时释放使用权,如果紧急等待队列不空,则唤醒第一个等待者,一般也由管程提供外部过程leave。
管程内部有自己的等待机制。管程可以说明一种特殊的条件型变量:var c:condition;实际上是一个指针,指向一个等待该条件的PCB队列。对条件型变量可执行wait和signal操作:
    wait(c):若紧急等待队列不空,唤醒第一个等待者,否则释放管程使用权。执行本操作的进程进入C队列尾部;
    signal(c):若C队列为空,继续原进程,否则唤醒队列第一个等待者,自己进入紧急等待队列尾部。

 


二. 实例
1. 生产者-消费者问题(有buffer)

问题描述:(见信号量部分)
解答:
  管程:buffer=MODULE;
   (假设已实现一基本管程monitor,提供enter,leave,signal,wait等操作)
     notfull,notempty:condition;
       — notfull控制缓冲区不满,notempty控制缓冲区不空;
     count,in,out: integer;
       — count记录共有几件物品,in记录第一个空缓冲区,out记录第一个不空的缓冲区
     buf:array [0..k-1] of item_type;
     define deposit,fetch;
     use monitor.enter,monitor.leave,monitor.wait,monitor.signal;
    procedure deposit(item);
    {
       if(count=k) monitor.wait(notfull);
       buf[in]=item;
       in:=(in+1) mod k;
       count++;
       monitor.signal(notempty);
     }
    procedure fetch:Item_type;
    {
       if(count=0) monitor.wait(notempty);
       item=buf[out];
       in:=(in+1) mod k;
       count--;
       monitor.signal(notfull);
       return(item);
     }
    {
       count=0;
       in=0;
       out=0;
     }

  进程:producer,consumer;
  producer(生产者进程):
   Item_Type item;
  {
     while (true)
     {
       produce(&item);
       buffer.enter();
       buffer.deposit(item);
       buffer.leave();
     }
  }

  consumer(消费者进程):
   Item_Type item;
  {
     while (true)
     {
       buffer.enter();
       item=buffer.fetch();
        buffer.leave();
       consume(&item);
     }
   }


 


2. 第一类读-写者问题

问题描述:(见信号量部分)
解答:
  管程:bulletin=MODULE;
   (假设已实现一基本管程monitor,提供enter,leave,signal,wait等操作)
     r,w:condition;
       — r控制读者使用黑板,w控制写者;
     writing:boolean;
       — 当前是否写者使用黑板
     read_account:integer;
     define start_read,end_read,start_write,end_write;
     use monitor.enter,monitor.leave,monitor.wait,monitor.signal;
    procedure start_read();
    {
       if(writing) monitor.wait(r);
       read_account++;
       monitor.signal(r);
     }
    procedure end_read();
    {
       read_account--;
       if (read_account=0) monitor.signal(w);
     }
    procedure start_write();
    {
       if((read_account<>0) or writing) monitor.wait(w);
       writing=true;
     }
    procedure end_write();
    {
       writing=false;
       if (r<>NULL) monitor.signal(r);
       else monitor.signal(w);
     }

  进程:writer - 写者进程,reader - 读者进程
  reader - (读者进程):
  {
     while (true)
     {
       bulletin.enter();
       bulletin.start_read();
       read();
       bulletin.end_read();
       bulletin.leave();
     }
  }

  writer - (写者进程):
  {
     while (true)
     {
       bulletin.enter();
       bulletin.start_write();
       write();
       bulletin.end_write();
       bulletin.leave();
     }
   }


 


3. 哲学家问题

问题描述:(见信号量部分)
解答:
  管程:dining=MODULE;
   (假设已实现一基本管程monitor,提供enter,leave,signal,wait等操作)
     queue:array [0..4] of condition;
       — 控制哲学家能否进食;
     fork:array[0..4] of (free,use);
       — 当前各个叉子的状态
     define pickup,test,putdown;
     use monitor.enter,monitor.leave,monitor.wait,monitor.signal;
    procedure pickup(i:0..4);
    {
       if(fork[i]=used) monitor.wait(queue[i]);
       fork[i]=used;
     }
    procedure putdown(i:0..4);
    {
       fork[i]=free;
       monitor.signal(queue[i]);
     }
    procedure test(i:0..4):boolean;
    {
       if(fork[i]=use) ok=false
       else { fork[i]=use; ok=true; } ;
       return ok;
     }

  管程:dining=MODULE;
  philosopher(i:0..4);
  with_two_forks:boolean;
  {
     while (true)
     {
       think();
       with_two_forks=false;;
       while(!with_two_forks) {
         dining.enter();
         dining.pickup(i);
         if(dining.test((i+1) mod 5) with_two_forks=true;
         else dining.putdown(i);
         dining.leave();
       }
       eat();
       dining.enter();
       dining.putdown(i);
       dining.putdown((i+1) mod 5));
       dining.leave();
     }
  }
### 管程机制的概念及其实现 #### 管程的基本概念 管程是一种用于解决并发编程中同步和互斥问题的经典机制。它的基本思想是由 C.A.R. Hoare 提出,通过封装共享变量以及对其的操作来实现线程间的协调[^1]。具体来说,管程将一组共享数据与其上的操作函数绑定在一起,并提供了特定的规则以确保这些操作能够安全地被执行。 #### 管程的作用 在并发环境中,管程主要解决了两大核心问题: 1. **互斥**:在同一时间点上仅允许一个线程访问某些临界区内的资源。这可以通过限制只有一个线程能进入管程内部来达成目标[^4]。 2. **同步**:允许多个线程间进行有效的通信与协作。为此,管程引入了条件变量和等待队列,使得线程可以在满足一定条件下继续运行或者暂时挂起直到条件成立为止。 #### Java 中的管程实现 虽然 Java 并未直接提供名为 “Monitor” 的关键字或类库支持显式的定义管程对象,但是其内置的关键字 `synchronized` 和相关 API 如 `wait()`、`notify()` 方法实际上隐含着基于管程原理的设计模式[^5]: - 使用 `synchronized` 块/方法可以构建类似于传统意义上描述的那种单入口多出口形式的监视器区域; - 而调用某个对象上的 `wait()` 则会让当前持有该对象锁的线程释放掉此锁定并加入到相应的等待集合当中;一旦其他地方触发了针对相同实例所关联的通知事件(`notify()`)之后,则会挑选其中一个合适的候选者使其脱离休眠状态进而尝试重新获取先前放弃的那个独占权限以便恢复正常的业务逻辑流程执行下去。 以下是利用上述特性模仿经典生产者消费者场景的一个简化版本代码示例: ```java class Buffer { private int content; private boolean available = false; public synchronized void produce(int value) throws InterruptedException { while (available) { // 防止多次写入 wait(); } content = value; available = true; notifyAll(); // 唤醒可能存在的消费者线程 } public synchronized int consume() throws InterruptedException { while (!available) { // 防止读取空缓冲区 wait(); } available = false; notifyAll(); // 唤醒可能存在的生产者线程 return content; } } public class ProducerConsumerExample { public static void main(String[] args) { final Buffer buffer = new Buffer(); Thread producer = new Thread(() -> { try { for (int i = 0; i < 5; ++i) { System.out.println("Producing " + i); buffer.produce(i); Thread.sleep(100); // Simulate delay } } catch (InterruptedException e) {} }); Thread consumer = new Thread(() -> { try { for (int i = 0; i < 5; ++i) { int consumedValue = buffer.consume(); System.out.println("Consumed " + consumedValue); Thread.sleep(100); // Simulate delay } } catch (InterruptedException e) {} }); producer.start(); consumer.start(); } } ``` 在这个例子里面展示了如何借助 java 自带的语言特性和标准库功能轻松搭建起一套遵循 monitor pattern 思路的应用框架出来. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值