管程就是一个软件模块,里面封装了实现同步,互斥的函数
1、为什么要引入管程
信号量机制存在的问题:编写程序困难、易出错
能不能设计一种机制,让程序员写程序时不需要再关注复杂的 PV
操作,让写代码更轻松呢?
1973年,Brinch Hansen
首次在程序设计语言(Pascal
)中引入了“管程”成分—―一种高级同步机制
2、管程的定义和基本特征
管程是一种特殊的软件模块,有这些部分组成:
-
局部于管程的 共享数据结构 \color{red}共享数据结构 共享数据结构说明;
-
对该数据结构进行操作的 一组过程 \color{red}一组过程 一组过程
-
对局部于管程的共享数据设置初始值的语句;
-
管程有一个名字。
Tips
:“过程” 其实就是 “函数”
管程的基本特征:
- 局部于管程的数据只能被局部于管程的过程所访问;
- 一个进程只有通过调用管程内的过程才能进入管程访问共享数据;
- 类似于 Java 中 private,要访问私有变量的数据,需要提供 public 方法才可以访问
- 每次仅允许一个进程在管程内执行某个内部过程 \color{red}每次仅允许一个进程在管程内执行某个内部过程 每次仅允许一个进程在管程内执行某个内部过程
例:若想访问这些数据结构的话,需要调用管程中的函数间接的访问这些数据
3、拓展1∶用管程解决生产者消费者问题
如下所示的伪代码:
除了由编译器负责实现各进程互斥地进管程中的过程之外
-
可以在管程中设置条件变量和等待/唤醒操作,已解决同步问题
-
例 2 :两个消费者进程先执行,生产者进程后执行…
引入管程的目的无非就是要更方便地实现进程互斥和同步。
-
需要在管程中定义共享数据(如生产者消费者问题的缓冲区)
-
需要在管程中定义用于访问这些共享数据的 “入口” ――其实就是一些函数(如生产者消费者问题中,可以定义一个函数用于将产品放入缓冲区,再定义一个函数用于从缓冲区取出产品)
-
只有通过这些特定的“入口”才能访问共享数据
-
管程中有很多 “入口”,但是每次只能开放其中一个“入口”,并且只能让一个进程或线程进入
(如生产者消费者问题中,各进程需要互斥地访问共享缓冲区。管程的这种特性即可保证一个时间段内最多只会有一个进程在访问缓冲区。)
注意 : 这种互斥特性是由编译器负责实现的,程序员不用关心 \color{red}注意:这种互斥特性是由编译器负责实现的,程序员不用关心 注意:这种互斥特性是由编译器负责实现的,程序员不用关心
-
可在管程中设置条件变量及等待/唤醒操作以解决同步问题。可以让一个进程或线程在条件变量上等待
(此时,该进程应先释放管程的使用权,也就是让出“入口”);可以通过唤醒操作将等待在条件变量上的进程或线程唤醒。
程序员可以用某种特殊的语法定义一个管程(比如: monitor ProducerConsumer .....end monitor
😉
- 之后其他程序员就可以使用这个管程提供的特定 “入口” 很方便地使用实现进程同步/互斥了。
- “封装” 思想
4、拓展2:Java 中类似于管程的机制
Java
中,如果用关键字 synchronized
来描述一个函数,那么这个函数同一时间段内只能被一个线程调用
每次只能有一个线程进入 insert
函数,如果多个线程同时调用 insert
函数,则后来者需要排队等待