引言
这偏文章主要是在我学习操作系统的时候遇到书上的一个关于信号量的例题,而自己在复习的时候依然卡在这个问题上好长时间,因此特别将这个问题写出,以防下次还忘记,例子比较简单,也欢迎大家发表自己的看法。
生产者消费者问题-(《操作系统-精髓与设计原理》第八版-p143页,图5.9)
/*program producerconsumer */
int n;
binary_semaphore s=1,dalay=0;
void producer(){
while(true){
produce();
semwaitB(s);
append();
n++;
if(n==1)semSignalB(delay);
semSignalB(s);
}
}
void consumer(){
semWaitB(delay);
while(true){
semWaitB(s);
take();
n--;
semSignalB(s);
consume();
if(n==0)semWaitB(delay);
}
}
void main(){
n=0;
parbegin(producer,consumer);
}
生产者消费者问题,主要是通过生产者生产出产品放入缓冲区,然后消费者从缓冲区中拿出产品消费。
生产者和消费者不能同时在临界区进行操作,同一时间只能有一个生产者或者一个消费者在临界区进行操作。
- 其中信号量s控制临界区是否上锁。信号量delay的作用是当缓冲区没有可消费物品时,阻塞消费者。两个信号量都是二元信号量。
- delay的初始值为0,在生产者中 if(n==1)semSignalB(delay)语句的目的是在生产者将生产出的产品放入缓冲区后,delay的值变为1,给消费者发信号,告诉消费者可以消费了。此时阻塞在semWaitB(delay)上的一个消费者进入就绪队列准备消费。当消费者发现缓冲区已经没有产品时,将自身阻塞,即if(n==0)semWaitB(delay)语句的作用.
- 但是当前的程序有问题,问题如下表
序号 | 生产者 | 消费者 | s | n | delay |
---|---|---|---|---|---|
1 | 1 | 0 | 1 | ||
2 | semwaitB(s); | 1 | 0 | 0 | |
3 | n++; | 0 | 1 | 0 | |
4 | if(n==1)semSignalB(delay); | 0 | 1 | 1 | |
5 | semSignalB(s); | 1 | 1 | 1 | |
6 | semWaitB(delay); | 1 | 1 | 0 | |
7 | semWaitB(s); | 0 | 1 | 0 | |
8 | n–; | 0 | 0 | 0 | |
9 | semSignalB(s); | 1 | 0 | 0 | |
10 | semwaitB(s); | 0 | 0 | 0 | |
11 | n++; | 0 | 1 | 0 | |
12 | if(n==1)semSignalB(delay); | 0 | 1 | 1 | |
13 | semSignalB(s); | 1 | 1 | 1 | |
14 | if(n==0)semWaitB(delay); | 1 | 1 | 1 | |
15 | semWaitB(s); | 0 | 1 | 1 | |
16 | n–; | 0 | 0 | 1 | |
17 | semSignalB(s) | 1 | 0 | 1 | |
18 | if(n==0)semWaitB(delay); | 1 | 0 | 0 | |
19 | semWaitB(s); | 0 | 0 | 0 | |
20 | n–; | 0 | -1 | 0 | |
21 | semSignalB(s) | 1 | -1 | 0 |
- 在第8行,此时产品被消费掉,n=0,此时应执行if(n==0)semWaitB(delay);,将进程本身阻塞,而此时生产者在第10行进入临界区,并将delay的值重新置为1,又生产了一个产品。此时再第14行执行if(n==0)semWaitB(delay)时并未生效,而是继续执行,在18行将delay置零,但是并未阻塞,于是又进行了一轮运行,最终得到n=-1,表示消费了还没有生产出的产品,显然这是错误的。产生这一错误的主要原因是执行顺序颠倒,应该先执行if(n==0)semWaitB(delay);,再执行生产者临界区。
解决方法:我曾经想过将if(n==0)semWaitB(delay)语句移入临界区执行,但是这一方法会产生死锁,因为还未退出临界区就将自己阻塞了。实际的解决方法应该是引入一个辅助变量,解决代码如下
/*program producerconsumer */
int n;
binary_semaphore s=1,dalay=0;
void producer(){
while(true){
produce();
semwaitB(s);
append();
n++;
if(n==1)semSignalB(delay);
semSignalB(s);
}
}
void consumer(){
int m;//引入辅助变量
semWaitB(delay);
while(true){
semWaitB(s);
take();
n--;
m=n;
semSignalB(s);
consume();
if(m==0)semWaitB(delay);//使用辅助变量完成执行
}
}
void main(){
n=0;
parbegin(producer,consumer);
}
结尾
写完这个博客,我有一个小问题,在生产者进程中,如果将“if(n==1)semSignalB(delay);”这个语句中的if(n==1)去掉,即每次只要一有生产者写入缓冲区,就发送semSignal信号是否可以呢?
我个人认为是可以的,欢迎在评论中回复我。