在条件变量的上下文中,虚假唤醒只是从服务员的角度来看。表示等待退出,但条件不成立;因此惯用用法是:
Thing.lock()
while Thing.state != Play {
Thing.wait()
}
....
Thing.unlock()
该循环的每次迭代,但一次,将被认为是虚假的。为什么会出现:
- 许多条件被复用到单个条件变量上;有时这是合适的,有时只是懒惰
- 等待线程将您的线程击败到条件,并在您有机会拥有它之前更改了它的状态。
- 不相关的事件,例如 kill(2) 处理这样做是为了确保异步处理程序运行后的一致性。
最重要的是验证是否满足期望的条件,如果不满足则重试或放弃。不重新检查很难诊断的情况是一个常见的错误。
作为一个更严肃的例子应该说明:
int q_next(Q *q, int idx) {
/* return the q index succeeding this, with wrap */
if (idx + 1 == q->len) {
return 0
} else {
return idx + 1
}
}
void q_get(Q *q, Item *p) {
Lock(q)
while (q->head == q->tail) {
Wait(q)
}
*p = q->data[q->tail]
if (q_next(q, q->head) == q->tail) {
/* q was full, now has space */
Broadcast(q)
}
q->tail = q_next(q, q->tail)
Unlock(q)
}
void q_put(Q *q, Item *p) {
Lock(q)
while (q_next(q, q->head) == q->tail) {
Wait(q)
}
q->data[q->head] = *p
if (q->head == q->tail) {
/* q was empty, data available */
Broadcast(q)
}
q->head = q_next(q, q->head)
Unlock(q)
}
这是一个多读者、多作者的队列。写入者等到队列中有空间时,将项目放入,如果队列之前为空,则广播以指示现在有数据。读者等到队列中有东西,从队列中取出项目,如果队列之前已满,则广播以指示现在有空间。
请注意,条件变量用于两个条件 {not full, not empty}。这些是边沿触发的条件:只有从满和从空的转换才会发出信号。
Q_get 和 q_put 保护自己免受由 [1] 和 [2] 引起的虚假唤醒,您可以轻松地检测代码以显示这种情况发生的频率。