1.管程(Monitor)
用信号量设计一个正确的程序是很困难的,难点是semWait和semSignal操作可能分布在整个程序中,但是很难看出这些在信号量上的操作所产生的整体效果。但是管程确易于设计。
管程是由一个或多个过程、一个初始化序列和局部数据组成的软件模块,其主要特点如下:
1) 局部数据变量只能被管程的过程访问,任何外部过程都不能访问(封装,因为管程就是一个对象)。
2) 一个进程通过调用管程的一个过程进入管程。
3) 任何时候,只能有一个进程在管程中执行,调用管程的任何其他进程都被阻塞,以等待管程可用(通过这一点就实现了互斥,管程天生实现互斥)。
管程通过使用条件变量提供对同步的支持,这些条件变量包含在管程中,并且只有在管程中才能被访问。有两个函数可以操作条件变量:
(在信号量的运用中,互斥是由信号量来实现的,各种条件也是有信号量来实现,所以信号量比较复杂,设计难度大,管程这两者分开考虑。)
cwait(c):调用进程的执行在条件c上挂起,管程现在可以被另一个进程使用。
csignal(c):恢复执行在cwait之后因为某些条件而挂起的进程。如果有多个这样的进程选择其中一个;如果没有这样的进程,什么也不做。
注意管程的wait和signal操作与信号量不同。如果在管程中的一个进程发信号,但没有这个条件变量上等待的任务,则丢弃这个信号。
2.管程结构
3.管程例子代码
下面给出一段用管程解决生产者/消费者问题的代码:
/* program producerconsumer */
monitor boundedbuffer;
char buffer [N]; /*N个元素大小的缓冲区*/
int nextin, nextout; /*缓冲区指针*/
int count; /*缓冲区中可用元素个数*/
cond notfull, notempty; /*条件变量*/
void append (char x)
{
if (count == N) cwait(notfull); /*缓冲区满,禁止写入,防止溢出*/
buffer[nextin] = x;
nextin = (nextin + 1) % N;
count++; /*多了一个数据*/
csignal (nonempty); /*通知消费者,缓冲区不空,可能有消费者阻塞*/
}
void take (char x)
{
if (count == 0) cwait(notempty); /*缓冲区空,禁止取数据,防止溢出*/
x = buffer[nextout];
nextout = (nextout + 1) % N);
count--; /*数据项少了一项*/
csignal (notfull); /*通知生产者,缓冲区不满,可能有生产者阻塞*/
}
{ /*管程体*/
nextin = 0; nextout = 0; count = 0; /*缓冲区初始化为空*/
}
void producer()
{
char x;
while (true) {
produce(x);
append(x);
}
}
void consumer()
{
char x;
while (true) {
take(x);
consume(x);
}
}
void main()
{
parbegin (producer, consumer);
}