用条件变量实现事件等待器的正确与错误作法 提到了8种 基于 linux pthread 条件变量实现的 Waiter classes,并分析了几种错误实现的错误之处。本文进一步分析一下几种正确实现的程序行为,加深对Linux pthread 条件变量的理解。html
下面给出一个能够用于single waiter的WaiterClass的正确实现。linux
class Waiter : private WaiterBase { public: void wait() { CHECK_SUCCESS(pthread_mutex_lock(&mutex_)); while (!signaled_) { CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_)); } CHECK_SUCCESS(pthread_mutex_unlock(&mutex_)); } void signal() { CHECK_SUCCESS(pthread_mutex_lock(&mutex_)); // 0 signaled_ = true; // 1 CHECK_SUCCESS(pthread_mutex_unlock(&mutex_)); // 2 CHECK_SUCCESS(pthread_cond_signal(&cond_)); // 3 } private: bool signaled_ = false; };
要实现一个正确的Waiter Class, 由 1 可知 wait() 函数的指令顺序必须如上所示。但signal() 函数能够有几种不一样的实现,即代码行 1,2,3能够有几种不一样的顺序组合,使得Waiter Class是正确的。这几种组合包括了1 给出的几种正确实现。函数
首先对 代码行1,2,3 给出全部可能的排列形式,而后一一说明其是否正确。
a. 1-2-3
b. 1-3-2
c. 2-1-3
d. 2-3-1
e. 3-1-2
f. 3-2-1测试
要分析以上6种实现的对错,先要了解一下 pthread_cond_wait 和 pthread_cond_signal 的内部流程。线程
假设等待信号的线程为A,发出信号的线程为B。code
线程A,pthread_cond_wait()内部包括如下流程:htm
线程B,pthread_cond_signal()包括如下流程:blog
signal 将一直唤醒G1组的 waiters 直到 G1 全部的 waiters 都被唤醒。若此过程种有新到达的waiter, 则存入G2 组(以后到达的 waiter 也存入G2)。G2组会在下次signal 调用时转为G1组,所以signal永远只唤醒G1 组的 waiters。(参考)队列
有了以上的细节,能够很容易的得出问题的结论。事件
总结发现,只要布尔值的修改是在释放锁的操做以前,就能保证其正确性。