pthread_cond_wait

pthread_cond_timedwait()和pthread_cond_wait()函数会阻塞在条件变量上。它们应和互斥锁(mutex lock)一起使用。


pthread_cond_wait内部操作

这些函数以原子方式释放互斥锁,并导致调用线程阻塞条件变量cond。这里的原子性考虑到了被其它线程获取锁并改变条件变量的情况。也就是,只有在这个线程阻塞了条件变量后,其他线程才能获取获取锁,改变条件变量,并调用pthread_cond_broadcast() or pthread_cond_signal()。更加准确地来说,是在将线程放到了等待队列之后,线程才会释放锁并挂起等待条件。

第一步:放到等待队列;

第二步:释放锁;

第三步:挂起等待条件。第二步和第三步是原子方式。

pthread_cond_wait会将该线程放到线程等待队列里,那么是在放到等待队列之前解锁还是放到等待队列之后才解锁呢?

对于这点apue给出的解释:The mutex passed to pthread_cond_wait protects the condition.The caller passes it locked to 
the function, which then atomically places the calling thread on the list of threads waiting for the condition and unlocks 
the mutex. This closes the window between the time that the condition is checked and the time that the
 thread goes to sleep waiting for the condition to change, so that the thread doesn't miss a change in the condition. 
When pthread_cond_wait returns, the mutex is again locked.


 这段话的意思是mutex传递给pthread_cond_wait 用于保护条件,调用者将mutex传递给pthread_cond_wait,
pthread_cond_wait 会自动将调用该函数的线程放到线程等待队列上等待条件,然后解锁。这种做法关闭了一段间隙,
这段间隙就是在我们检测条件的时刻和将线程放到等待队列休眠的时刻之间,这么做该线程不会错过条件的改变。而当
pthread_cond_wait 返回时,mutex又被上锁了。
所以,pthread_cond_wait内部的操作顺序是将线程放到等待队列,然后解锁,等条件满足时重新竞争锁,竞争到后加锁,然后返回。
整理下pthread_cond_wait内部操作
1,线程放在等待队列上,解锁
2,等待 pthread_cond_signal或者pthread_cond_broadcast信号之后去竞争锁
3,若竞争到互斥锁则加锁。

条件变量的虚假唤醒(spurious wakeups)问题

使用pthread_cond_timedwait()或者pthread_cond_wait()方法可能发生虚假唤醒。因为从pthread_cond_timedwait()或者pthread_cond_wait()方法返回并不意味着该条件变量值的任何内容。所以应该在返回后重新判断该条件变量值。

pthread_cond_wait的通常使用方法为:

pthread_mutex_lock();
while(condition_is_false){
  pthread_cond_wait();
}    
pthread_mutex_unlock();

我们必须把判断布尔条件和wait()放到while循环中,而不能用if语句,原因是可能会引起虚假唤醒。

spurious wakeup的发生通常有2个原因:

  • A thread blocked in pthread_cond_wait can return from the call even though no call to signal or broadcast on the condition occurred.
  • A thread blocked in pthread_cond_wait returns because of a call to signal or broadcast, however after reacquiring the mutex the underlying predicate is found to no longer be true.

第一个原因是,即使没有发出signal或者broadcast信号,一个阻塞在pthread_cond_wait的线程也可以返回。

第二个原因:因为发出signal或者broadcast信号,一个阻塞在pthread_cond_wait的线程返回,然后在重新获得锁后,条件变量值不再满足条件。

对第二个原因举个例子,我们现在有一个生产者-消费者队列和三个线程。

1) 1号线程从队列中获取了一个元素,此时队列变为空。

2) 2号线程也想从队列中获取一个元素,但此时队列为空,2号线程便只能进入阻塞(cond.wait()),等待队列非空。

3) 这时,3号线程将一个元素入队,并调用cond.notify()唤醒条件变量。

4) 处于等待状态的2号线程接收到3号线程的唤醒信号,便准备解除阻塞状态,执行接下来的任务(获取队列中的元素)。

5) 然而可能出现这样的情况:当2号线程准备获得队列的锁,去获取队列中的元素时,此时1号线程刚好执行完之前的元素操作,返回再去请求队列中的元素,1号线程便获得队列的锁,检查到队列非空,就获取到了3号线程刚刚入队的元素,然后释放队列锁。

6) 等到2号线程获得队列锁,判断发现队列仍为空,1号线程“偷走了”这个元素,所以对于2号线程而言,这次唤醒就是“虚假”的,它需要再次等待队列非空。


参考:

pthread_cond_wait(3) - Linux man page

http://stackoverflow.com/questions/8594591/why-does-pthread-cond-wait-have-spurious-wakeups

条件变量的虚假唤醒(spurious wakeups)问题


猜你喜欢

转载自blog.csdn.net/qq_26222859/article/details/81064360