简体   繁体   English

线程安全队列和虚假唤醒

[英]Thread safe queues and spurious wakes

I am currently reading a book about multi threading in C++. 我目前正在阅读有关C ++中多线程的书。 In one chapter I found some source code for a thread safe queue. 在第一章中,我找到了线程安全队列的一些源代码。 It is roughly built like this: 它大致是这样构建的:

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();
    }
};

When a value is pushed to the queue, a data condition is notified and some (possible) waiting thread in pop can resume working. 当将值推入队列时,将通知数据条件,并且弹出中的某些(可能)等待线程可以恢复工作。 What confuses me are spurious wakes in this scenario. 使我感到困惑的是这种情况下的虚假唤醒。 What if, at the same time one thread is notified, another thread just wakes up at the same time? 如果在同一时间通知一个线程,而另一个线程在同一时间唤醒,该怎么办? Of course he also sees a not empty queue. 当然,他也看到一个不空的队列。 In this scenario, two different threads would try to pop a value, where possibly only one value exists - a classical race condition. 在这种情况下,两个不同的线程将尝试弹出一个值,其中可能仅存在一个值-经典竞争条件。

Did I miss something here? 我在这里想念什么吗? Is there a better way to do this? 有一个更好的方法吗?

Spurious wakes just mean you need to check that the condition for the wake remains valid when you are woken. 虚假唤醒仅意味着您需要在唤醒时检查唤醒条件是否仍然有效。 Since the wait function is passed: 由于传递了wait函数:

  1. A lock, for mutual exclusion, and 用于互斥的锁,以及
  2. A predicate to determine if the wait has been satisfied 判断是否满足等待条件的谓词

the behavior when one thread is notified "normally", and another is notified spuriously is that one of them (doesn't matter which, whichever races faster) acquires the lock and confirms the queue is non-empty, then pops off the top element and releases the lock; 当一个线程被“正常”通知而另一个线程被虚假通知时的行为是,其中一个线程(无论哪个,以更快的速度运行)获取锁并确认队列为非空,然后弹出顶部元素并释放锁; the one that lost the race for the lock doesn't acquire the lock until the faster thread releases the lock, so it sees the already emptied queue and decides it was a spurious wakeup, going back to sleep. 在较快的线程释放锁之前,丢失争用锁的线程不会获取锁,因此它会看到已经清空的队列,并确定这是一个虚假的唤醒,然后重新进入睡眠状态。

Importantly, it doesn't really matter whether the spuriously woken thread won the race for the lock (and the queued item) or not; 重要的是,虚假唤醒的线程是否赢得了争夺锁(和排队的项目)的竞争并不重要。 one of the threads behaved as if woken normally (it found the condition true and worked as expected), one as if woken spuriously (it found the condition false and went back to waiting, as expected), and the code as a whole behaved correctly. 其中一个线程的行为好像正常唤醒(发现条件为真并按预期工作),一个线程的行为好像是虚假唤醒(发现条件为假并按预期返回等待状态),并且代码整体上表现正常。

I think in that situation, the thread notified and the waked up thread have the same opportunity to pop from the queue, it only depends how the CPU make the schedule decision(which one is faster). 我认为在这种情况下,通知的线程和唤醒的线程有相同的机会从队列中弹出,这仅取决于CPU如何做出调度决定(哪个更快)。

Unless you want specify which thread should have the right., then you have to change the implementation. 除非您想指定哪个线程应该具有权限,否则必须更改实现。

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

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