在前面 进程 中提到,有信号量与互斥量之后,似乎进程间通信就很容易了。但事实上并不然,PV操作不慎是很容易导致两个进程都被阻塞的,这种情况就叫做死锁(Dead Lock)。
为了更容易编写出正确的程序,1973和1974年Hansen与Hoare提出了另一种高级同步原语,称为管程(Monitor)。
但他们提出的方式略微有些差别,不过核心都是资源集中管理,将系统中资源通过抽象的形式描述。
管程是一个由过程、变量、数据结构等组成的集合,它们共同形成一个模块或是软件包。进程可以在任何需要的时候调用管程中的过程,但不能在管程声明之外的过程中访问管程内的数据结构。
还提供了一种 条件结构(Condition Construct) 的机制来保障多进程安全、有效的共享数据。如为实现进程互斥同步,可以定义一些条件变量,这些条件变量只能被wait
或者signal
操作访问。前者表示调用该操作的进程将挂起,而后者是启动一个被挂起的进程,但与V操作不同的是后者不会改变条件变量的状态,如果失败则等于空操作。
假定要仍然用来解决生产者与消费者问题,则可以定义notfull, notempty
的条件变量,类语言过程如下:
Type Producer-Consumer=monitor
var buffer : array[0,...,n-1] of item;
in, out, count : interger;
notfull, notempty : condition; //定义条件变量
procedure entry put(item) //将产品放入缓冲区
begin
if count >= n then notfull.wait; //缓冲区满则等待
buffer(in):=nextp;
in:=(in+1) mod n;
count:=count+1;
if notempty.queue then notempty.signal; //唤醒等待者
end
procedure entry get(item)
begin
if count <= 0 then notempty.wait; //缓冲区空等待
nextc:=buffer(out);
out:=(out+1) mod n;
count:=count+1;
if notfull.queue then notfull.signal //唤醒生产者
end
begin in:=out:=0; count:=0; end //初始化
cobegin
producer : begin
repeat
produce an item in nextp;
Producer-Consumer.put(item);
until false;
end
consumer:begin
repeat
Producer-Consumer.get(item);
consume the item in nextc;
until false;
end
coend
其实管程概念实际上是一种将互斥或信号包装的做法,对管程的支持基本都由编程语言及编译器实现,这样一来就可以减少人为失误导致的死锁,也使的编写大大简便。