调用condition_variable等待函数时线程如何等待?

Posted

技术标签:

【中文标题】调用condition_variable等待函数时线程如何等待?【英文标题】:How do threads wait when condition_variable wait function is called? 【发布时间】:2017-10-19 19:26:58 【问题描述】:

假设我有两组线程。一组的功能是向数组中添加一个元素,另一组的功能是如果数组包含相同的元素,则从数组中删除一个元素。规则是线程不能从数组中删除一个元素,如果它是空的并且它必须等待。监视器用于解决此同步问题。

考虑一个场景,所有线程同时启动,消费者线程先锁定互斥体,然后检查数组是否为空,条件为假,从而解锁互斥体。然后生产者线程首先锁定互斥体,添加一个元素并通知所有等待线程并解锁互斥体。问题是,等待线程在收到通知后是否首先获得对互斥锁的访问权,并且等待线程可以尝试删除元素,或者互斥锁再次空闲并且任何线程可以再次偶然锁定它,而等待线程不是在条件失败后完成,但假设它被放回线程池。

【问题讨论】:

你最好显示一些(伪)代码,很难理解你在问什么。 【参考方案1】:

首先,让我们明确一些事情(包含cppreference's page about std::condition_variable的精髓):

消费者

消费者waitstd::condition_variable-s 上使用以下步骤:

在用于保护共享变量的同一互斥体上获取std::unique_lock<std::mutex> 执行 wait、wait_for 或 wait_until。等待操作以原子方式释放互斥锁并暂停线程的执行。 当通知条件变量、超时到期或发生虚假唤醒时,线程被唤醒,并以原子方式重新获取互斥锁。 线程应该检查条件,如果唤醒是虚假的,则继续等待

制片人

生产者notifystd::condition_variable-s 遵循以下步骤:

获取 std::mutex(通常通过 std::lock_guard) 在持有锁时执行修改 在std::condition_variable上执行notify_onenotify_all(通知不需要保持锁)

回答

等待线程在收到通知后是否首先获得对互斥锁的访问权,并且等待线程可以尝试删除元素

是的,可能有多个消费者在同一条件下,每个消费者都可能消费单个对象,这就是为什么每个等待线程都应该使用额外的逻辑条件来防止虚假唤醒(参见粗体消费者部分中的文本)。 std::condition_variablewait 方法甚至有一个已经包含它的特定原型:

template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate pred );

当消费者醒来时,它已经获得了锁!所以,如果满足条件(例如!queue-&gt;empty()),就可以安全消费了。

【讨论】:

以上是关于调用condition_variable等待函数时线程如何等待?的主要内容,如果未能解决你的问题,请参考以下文章

std::atomic 和 std::condition_variable 等待、notify_* 方法之间的区别

如何正确使用 std::condition_variable?

在等待通知 std::condition_variable 期间执行“等待回调”

等待条件的线程的有序通知(C++,boost)

线程 condition_variable

C++11的condition_variable实现WaitForSingleObject功能