繁体   English   中英

线程安全队列和虚假唤醒

[英]Thread safe queues and spurious wakes

我目前正在阅读有关C ++中多线程的书。 在第一章中,我找到了线程安全队列的一些源代码。 它大致是这样构建的:

template<typename T>
class QueueThreadSafe
{
private:
    std::mutex m_mutex;
    std::queue<T> m_dataQueue;
    std::condition_variable m_dataCondition;

public:
    void push(T someValue)
    {
        std::lock_guard<std::mutex> guard(m_mutex);
        m_dataQueue.push(someValue);
        m_dataCondition.notify_one();
    }

    void pop(T &retVal)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_dataCondition.wait(lock, [this]{return !m_dataQueue.empty();});
        retVal = m_dataQueue.front();
        m_dataQueue.pop();
    }
};

当将值推入队列时,将通知数据条件,并且弹出中的某些(可能)等待线程可以恢复工作。 使我感到困惑的是这种情况下的虚假唤醒。 如果在同一时间通知一个线程,而另一个线程在同一时间唤醒,该怎么办? 当然,他也看到一个不空的队列。 在这种情况下,两个不同的线程将尝试弹出一个值,其中可能仅存在一个值-经典竞争条件。

我在这里想念什么吗? 有一个更好的方法吗?

虚假唤醒仅意味着您需要在唤醒时检查唤醒条件是否仍然有效。 由于传递了wait函数:

  1. 用于互斥的锁,以及
  2. 判断是否满足等待条件的谓词

当一个线程被“正常”通知而另一个线程被虚假通知时的行为是,其中一个线程(无论哪个,以更快的速度运行)获取锁并确认队列为非空,然后弹出顶部元素并释放锁; 在较快的线程释放锁之前,丢失争用锁的线程不会获取锁,因此它会看到已经清空的队列,并确定这是一个虚假的唤醒,然后重新进入睡眠状态。

重要的是,虚假唤醒的线程是否赢得了争夺锁(和排队的项目)的竞争并不重要。 其中一个线程的行为好像正常唤醒(发现条件为真并按预期工作),一个线程的行为好像是虚假唤醒(发现条件为假并按预期返回等待状态),并且代码整体上表现正常。

我认为在这种情况下,通知的线程和唤醒的线程有相同的机会从队列中弹出,这仅取决于CPU如何做出调度决定(哪个更快)。

除非您想指定哪个线程应该具有权限,否则必须更改实现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM