生产者和消费者问题中的唤醒丢失(操作系统)
现在回到竞争条件的问题。这里有可能会出现竞争条件,其原因是对count的访问未加限制。有可能出现以下情况:缓冲区为空,消费者刚刚读取count的值发现它为0。此时调度程序决定暂停消费者并启动运行生产者。生产者向缓冲区中加入一个数据项,count加1。现在count的值变成了1。它推断认为由于count刚才为0,所以消费者此时一定在睡眠,于是生产者调用wakeup来唤醒消费者。
————
代码如下:
#define N 100 int count = 0; void producer(void) { int item; while(TRUE) { item = produce_item(); if(count == N) //如果缓冲区满就休眠 sleep(); insert_item(item); count = count + 1; //缓冲区数据项计数加1 if(count == 1) wakeup(consumer); } } void consumer(void) { int item; while(TRUE) { if(count == 0) //如果缓冲区空就休眠 sleep(); item = remove_item(); count = count - 1; //缓冲区数据项计数减1 if(count == N - 1) wakeup(producer); consume_item(item); } }
但是,消费者此时在逻辑上并未睡眠,所以wakeup信号丢失。当消**费者下次运行时,它将测试先前读到的count值,发现它为0,于是睡眠。**生产者迟早会填满整个缓冲区,然后睡眠。这样一来,两个进程都将永远睡眠下去。
在这里“逻辑上”是指
-----------当消费者进程检测到count等于0时,在还没有运行sleep()时消费者进程时间片到期,消费者进程变成runnable状态,生产者这时候被调度运行,当count变为1时,生产者试图唤醒消费者,此时对于消费者来说唤醒是无效的,因为消费者根本没有运行到sleep(),,wakeup信号丢失,所以说在逻辑上并未睡眠。当时间片被转到消费者时,这时候消费者进程被调度,执行完sleep()后,消费者进程才真正进入逻辑上得睡眠。尽管count已经非零了,但是再也不会有唤醒信号了,它将永远沉睡。归根到底是因为这里对count的访问不是原子性的。