简体   繁体   English

std :: condition_variable虚假阻塞

[英]std::condition_variable spurious blocking

As you know, condition variables should be called in cycle to avoid spurious wake-ups. 如您所知,应该在循环中调用条件变量以避免虚假唤醒。 Like this: 像这样:

while (not condition)
    condvar.wait();

If another thread wants to wake up waiting thread, it must set condition flag to true. 如果另一个线程想要唤醒等待线程,则必须将condition标志设置为true。 Eg: 例如:

condition = true;
condvar.notify_one();

I wonder, is it possible for condition variable to be blocked by this scenario: 我想知道,这种情况是否有可能阻止条件变量:

1)Waiting thread checks condition flag, and finds it is equal to FALSE, so, it's going to enter condvar.wait() routine. 1)等待线程检查条件标志,并发现它等于FALSE,因此,它将进入condvar.wait()例程。

2)But just before this (but after condition flag checking) waiting thread is preempted by kernel (eg because of time slot expiration). 2)但就在此之前(但是在条件标志检查之后)等待线程被内核抢占(例如,因为时隙到期)。

3) At this time, another thread wants to notify waiting thread about condition. 3)此时,另一个线程想要通知等待线程有关条件。 It sets condition flag to TRUE and calls condvar.notify_one(); 它将条件标志设置为TRUE并调用condvar.notify_one();

4) When kernel scheduler runs first thread again, it enters condvar.wait() routine, but the notification have been already missed. 4)当内核调度程序再次运行第一个线程时,它进入condvar.wait()例程,但通知已经被遗漏。

So, waiting thread can't exit from condvar.wait() , despite condition flag is set to TRUE, because there is no wake up notifications anymore. 因此,等待线程无法退出condvar.wait() ,尽管条件标志设置为TRUE,因为不再有唤醒通知。

Is it possible? 可能吗?

That is exactly why a condition variable must be used in conjunction with a mutex, in order to atomically update the state and signal the change. 这正是为什么条件变量必须与互斥锁一起使用才能以原子方式更新状态并发出变化信号的原因。 The full code would look more like: 完整代码看起来更像:

unique_lock<mutex> lock(mutex);
while (not condition)
    condvar.wait(lock);

and for the other thread: 而对于另一个线程:

lock_guard<mutex> lock(mutex);
condition = true;
condvar.notify_one();

You example missing small part, but that explains why that is not possible if done correctly: 您的示例缺少小部分,但这解释了为什么如果正确完成则无法实现:

while (not condition) // when you check condition mutex is locked
    condvar.wait( mutex ); // when you wait mutex is unlocked

So if you change condition to true under the same mutex lock, this situation will not happen. 因此,如果在相同的互斥锁下将条件更改为true,则不会发生这种情况。

Mike Seymour his answer is incomplete because there is a race condition which ends up with wakeup lost. Mike Seymour他的答案是不完整的,因为有一种竞争条件最终导致唤醒失败。 The right way is to (now with the c++11) is as follow: 正确的方法是(现在使用c ++ 11)如下:

Thread1: 线程1:

std::unique_lock<std::mutex> lck(myMutex);
condvar.wait(lck, []{ return condition; }); // prevent spurious wakeup
// Process data

Thread2: 线程2:

{
    std::lock_guard<std::mutex> lck(myMutex);
    condition = true;
} // unlock here! prevent wakeup lost
condvar.notify_one();

Yes (I tested this in December 2012), and there is a solution I conjured up for this a while ago. 是的(我在2012年12月对此进行了测试),并且有一个我刚才想到的解决方案。 The "Flare" class: Note that it uses a spin lock, but the time spent in this is minimal. “Flare”类:请注意,它使用自旋锁,但花费的时间很少。

Declaration (hpp): 声明(hpp):

class Flare
{
public:
/**
\brief Flare's constructor.
\param fall_through_first, will skip the first wait() if true.
*/
Flare(bool fall_through_first = false);


/**
\brief Flare's destructor.

Takes care of removing the object of this class.
*/
~Flare();


/**
\brief Notifies the same object of availability.

Any thread waiting on this object will be freed,
and if the thread was not waiting, it will skip
wait when it iterates over it.
*/
void notify();


/**
\brief Wait until the next notification.

If a notification was sent whilst not being
inside wait, then wait will simply be skipped.
*/
void wait();


private:
    std::mutex m_mx; // Used in the unique_lock,
    std::unique_lock<std::mutex> m_lk; // Used in the cnd_var
    std::condition_variable m_cndvar;

    std::mutex m_in_function, n_mx; // protection of re-iteration.
    bool m_notifications;

};

Implementaton/Definition (cpp): 实现/定义(cpp):

#include "Flare.hpp"


// PUBLIC:

Flare::Flare(bool fall_through_first)
:
m_lk(m_mx),
m_notifications(!fall_through_first)
{}

Flare::~Flare()
{}

void Flare::notify()
{
    if (m_in_function.try_lock() == true)
    {
        m_notifications = false;
        m_in_function.unlock();
    }
    else // Function is waiting.
    {
        n_mx.lock();
        do
        {
            m_notifications = false;
            m_cndvar.notify_one();
        }
        while (m_in_function.try_lock() == false);
        n_mx.unlock();
        m_in_function.unlock();
    }
}

void Flare::wait()
{
    m_in_function.lock();
    while (m_notifications)
        m_cndvar.wait(m_lk);
    m_in_function.unlock();
    n_mx.lock();
    m_notifications = true;
    n_mx.unlock();
}

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

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