C++11下条件变量正确用法

转载声明

————————————————

版权声明:本文为CSDN博主「陈硕」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Solstice/article/details/11432817

正文:

  • 条件变量只有一种正确使用的方式,几乎不可能用错。对于 wait 端:
  1. 必须与 mutex 一起使用,该布尔表达式的读写需受此 mutex 保护。
  2. 在 mutex 已上锁的时候才能调用 wait()。
  3. 把判断布尔条件和 wait() 放到 while 循环中。

代码示例:

T take()
{
    std::unique_lock<std::mutex> lock(mutex_);
    // always use a while-loop, due to spurious wakeup
    while (queue_.empty())
    {
        notEmpty_.wait(lock);
    }
    assert(!queue_.empty());
    T front(queue_.front());
    queue_.pop_front();
    return front;
}
  • 对于 signal/broadcast 端:
  1. 不一定要在 mutex 已上锁的情况下调用 signal (理论上)。
  2. 在 signal 之前一般要修改布尔表达式。
  3. 修改布尔表达式通常要用 mutex 保护(至少用作 full memory barrier)。
  4. 注意区分 signal 与 broadcast:“broadcast 通常用于表明状态变化,signal 通常用于表示资源可用。(broadcast should generally be used to indicate state change rather than resource availability。)”

代码示例:

void put(const T& x)
{
    std::lock_guard<std::mutex> lock(mutex_);
    queue_.push_back(x);
    notEmpty_.notify_one();
}

为什么用while而不用if

  • 因为可能某次操作系统唤醒 pthread_cond_wait 时 tasks.empty() 可能仍然为 true,言下之意就是操作系统可能会在一些情况下唤醒条件变量,即使没有其他线程向条件变量发送信号,等待此条件变量的线程也有可能会醒来。我们将条件变量的这种行为称之为 虚假唤醒 (spurious wakeup)。因此将条件(判断tasks.empty() 为true)放在一个 while 循环中意味着光唤醒条件变量不行,还必须条件满足程序才能继续执行正常的逻辑。
  • 为什么会存在虚假唤醒呢?一个原因是
    pthread_cond_wait 是 futex 系统调用,属于阻塞型的系统调用,当系统调用被信号中断的时候,会返回 -1,并且把 errno 错误码置为EINTR。很多这种系统调用为了防止被信号中断都会重启系统调用(即再次调用一次这个函数)
  • 除了上面的信号因素外,还存在以下情况:条件满足了发送信号,但等到调用 pthread_cond_wait 的线程得到 CPU 资源时,条件又再次不满足了。好在无论是哪种情况,醒来之后再次测试条件是否满足就可以解决虚假等待的问题。这就是使用 while 循环来判断条件,而不是使用 if 语句的原因。

事件等待器的实现:

#include <mutex>
#include <condition_variable>
class CountDownLatch
{
public:
    explicit CountDownLatch(int count) :count_(count)
    {
    }
    void wait()
    {
        std::unique_lock<std::mutex> lock(mutex_);
        while (count_ > 0)
            condition_.wait(lock);
    }
    void countDown()
    {
        std::unique_lock<std::mutex> lock(mutex_);
        --count_;
        if (count_ == 0)
            condition_.notify_all();
    }

private:
    std::mutex mutex_;
    std::condition_variable condition_;
    int count_;
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容