使用条件变量实现事件等待器的正确方法和错误方法提到了8种基于linuxpthread条件变量的Waiter类,分析了几种错误实现的错误。本文进一步分析了几个正确实现的程序行为,加深了对Linuxpthread条件变量的理解。下面给出了可用于单个服务员的WaiterClass的正确实现。类Waiter:privateWaiterBase{public:voidwait(){CHECK_SUCCESS(pthread_mutex_lock(&mutex_));while(!signaled_){CHECK_SUCCESS(pthread_cond_wait(&cond_,&mutex_));}CHECK_SUCCESS(pthread_mutex_unlock)(&mutex_)(){CHECK_SUCCESS(pthread_mutex_lock(&mutex_));//0signaled_=true;//1CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));//2CHECK_SUCCESS(pthread_cond_signal(&cond_));//3}私有:boolsignaled_=false};要实现一个正确的WaiterClass,由1可知wait()函数的指令顺序必须如上所示。但是signal()函数可以有几种不同的实现,即代码1、2、3的行可以有几种不同的组合,以便Waiter类是正确的。这些组合包括1中给出的几个正确实现,先给出代码行1,2,3所有可能的排列,然后一一说明是否正确。A。1-2-3b。1-3-2c。2-1-3d。2-3-1e。3-1-2f。查看pthread_cond_wait和pthread_cond_signal的内部过程。假设等待信号的线程是A,发送信号的线程是B。线程A,pthread_cond_wait()内部包括如下过程:将waiter添加到cond的_wseq队列中(分为G1和G2两组),释放mutex自旋等待,检查__g_signals,自旋次数结束,进入futex_wait,sleep线程B,pthread_cond_signal()包括以下过程:检查cond__wseq,没有waiter直接返回。有waiter,检查是否需要切换组(比如第一次调用wait后G1为空,而G2有waiter,那么需要在第一次调用signal后将G2切换为G1),自增__g_signals,递减__g_size(未唤醒的服务员数),然后调用futex_wake。该信号会唤醒G1组的服务员,直到G1组的所有服务员都被唤醒。如果在这个过程中有新到的服务员,则将其存入G2组(以后到的服务员也会存入G2)。G2组会在下次信号调用时转移到G1组,所以信号永远只会唤醒G1组的waiters。(参考)有了上面的细节,问题的结论就很容易得出了。首先应排除e和f。ef拿到锁后立即释放锁。1和2不受锁保护。1和2的执行时机可以随意穿插在wait()函数的每条语句中,所以语句2发出的信号很可能会丢失。不正确。导致线程A饿死的序列:2发送信号,唤醒线程A,3释放锁,A获取锁,判断布尔值,条件为真,再次进入等待,并一直睡空调是对的。不管发信号时boolean值是否被修改,被唤醒的线程A永远得不到mutex,直到3释放锁,futex_wake通知线程A可以拿到锁,从pthread_cond_wait返回,继续往下执行。此时boolean值已经改变,条件测试为false,跳出循环,继续执行。事实上,由于编译器乱序和CPU乱序,ac可能以相同的顺序运行。b也是正确的。线程B修改布尔变量后释放锁。此时线程A还处于休眠状态,不知道发生了什么。只有当B发出信号唤醒A,A才能看到这一切,成功获得锁,然后继续Next执行。综上,发现只要对布尔值的修改在释放锁操作之前,就可以保证其正确性。
