【问题标题】:Conditional variable wakes up before it's been notified条件变量在被通知之前唤醒
【发布时间】:2019-01-31 11:39:23
【问题描述】:

我需要一个线程安全的队列,如果它不为空则产生数据,并等待数据到达。超时。我有这样的

void ThreadsafeQueue::enqueue(data_t& data)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_queue.push(data);

    m_condvar.notify_one();
}

boost::optional<data_t> ThreadsafeQueue::dequeue()
{
    std::unique_lock<std::mutex> lock(m_mutex);
    const std::chrono::seconds DEFAULT_DELAY(10);
    if ((!m_queue.empty()) || m_condvar.wait_for(lock, DEFAULT_DELAY) == std::cv_status::no_timeout)
    {
        const auto data = m_queue.front();
        m_queue.pop();
        return data;
    }

    return boost::none;
}

但是,由于某种原因,有时它有时会进入带有 no_timeout 的 if 语句,而实际上它并没有得到通知。并尝试在空队列上使用 front()。

我做错了吗?

【问题讨论】:

标签: c++ std


【解决方案1】:

由于可能会通过启动条件变量来唤醒多个线程,或者有时会发生虚假唤醒(1),因此您应该检查唤醒谓词以确保它真的。

换句话说,你的dequeue 代码最好是这样的:

boost::optional<data_t> ThreadsafeQueue::dequeue() {
    std::unique_lock<std::mutex> lock(m_mutex);
    static const std::chrono::seconds DEFAULT_DELAY(10);

    // Only wait on empty queue, return none on timeout.
    if (m_queue.empty())
        if (m_condvar.wait_for(lock, DEFAULT_DELAY) == std::cv_status::timeout)
            return boost::none;

    // Catch spurious wake-up.
    if (m_queue.empty())
        return boost::none;

    // Have item in queue, extract and return it.
    const auto data = m_queue.front();
    m_queue.pop();
    return data;
}

(1) 根据here(我的重点):

自动释放锁,阻塞当前正在执行的线程,并将其添加到等待*this的线程列表中。当notify_all()notify_one() 被执行,或相对超时rel_time 到期时,线程将被解除阻塞。 它也可能被虚假解锁。

【讨论】:

  • 但即使队列为空,它也会等待。如果队列不为空,我不希望它等待:D
  • @Eugene:进行了修改以考虑到这一点。
  • 不应该是||,而不是&&? :D
  • @Eugene:是的,但为了便于阅读,我已经将检查分开,所以现在可以了。
  • if (m_queue.empty()) return boost::none;为什么它返回 boost::none 如果它是空的?我的意思是,第一次
【解决方案2】:

即使没有通知,系统有时也会唤醒条件变量。这就是为什么在唤醒之后,你需要检查条件是否真的满足或者再次等待。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-13
    • 2019-09-27
    • 2017-11-09
    • 1970-01-01
    • 2013-07-12
    • 2011-11-24
    • 1970-01-01
    相关资源
    最近更新 更多