接下来,给出 pthread_cond_wait 和 pthread_cond_signal 的伪代码(参考man pthread_cond_signal)。语句后面的编号代表时间上的执行顺序。
对于一般情况,wait 完成进入等待队列的情况比较简单,就不考虑了。我们考虑在释放互斥锁后,进入 wait 等待队列前的情况:
假设只有两个线程 A 和 B
线程 A 执行完语句 2 后正在尝试进入等待队列(已经解锁但是尚未阻塞),即期望执行语句 10 以及后面的进入等待队列. 同时线程 B 正在执行 pthread_cond_signal 中的语句 3 到语句 9. 此时等待队列为空,所以 if (cond->waiter) 条件不成立。
另外条件也发生了改变(),线程 B 完成了 pthread_signal 返回后,线程 A 执行语句 10,因为条件已经成立,所以线程 A 不会进入等待队列直接返回。宏观上看起来,好像就是从“等待队列”中被唤醒一样。
如果你还记得在上一篇条件变量中的叙述:
pthread_cond_wait 被分解成了三步,其中 a1 和 a2 是一次执行完的。这两个步骤是原子的。
而实际上,pthread_cond_wait 的实现应该像本文上面的伪代码, 即阻塞不是必须的,能给人一种错觉就够了。
再次回顾一下前文所述:
如果线程 A 在释放锁后(语句 a1),执行语句 a2 的即将要阻塞的时候,线程 B 此时调用 pthread_cond_signal 或者 pthread_cond_broadcast ,感觉就好像即将要被阻塞的线程 A 已经阻塞过一样。
现在你应该理解了所谓的“原子”是怎么做的了吧。
英文术语叫 spurious wakeup。
在前一篇文章中说,pthread_cond_signal 可以唤醒队列中的一个线程,而实际上,它也可能唤醒不止一个线程。
man 手册做此解释::
在多核系统中,要避免 pthread_cond_signal 唤醒超过一个以上的线程,似乎是不可能的。
继续使用前面使用的伪代码。
假设只有三个线程 A 、B 和 C
线程 A 执行完语句 2 后正在尝试进入等待队列(已经解锁但是尚未阻塞),即期望执行语句 10 以及后面的进入等待队列. 同时线程 B 正在执行 pthread_cond_signal 中的语句 3 到语句 9. 另一方面,线程 C 正处于等待队列中。
和第 1 节中不一样的是,语句 5 成立,因为队列中有线程 C。因此 pthread_cond_signal 会唤醒线程 C。而线程 A 也会因为 直接执行语句13. 记住,此时的 A 是唤醒状态,不是位于等待队列中。
一旦线程 C 释放锁后,线程 A 就返回,然而此时,条件可能已经不成立(比如条件被程 C 更改),故出现虚假唤醒的状态。
实际上在上一篇的实验中已经给出了解决方案,即即使 pthread_cond_signal 已经返回,也不意味着条件一定成立,代码中使用了循环 反复测试。
切记,这里不要使用 ,而是 !!! 目的在于防止虚假唤醒——spurious wakeup.
- 深入理解条件变量的实现
- 什么是虚拟唤醒
- 如何避免虚假唤醒
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/cjjbc/73041.html