std :: condition_variable虚假阻塞

时间:2013-02-25 17:11:57

标签: c++ c++11 condition-variable

如您所知,应在循环中调用条件变量以避免虚假唤醒。像这样:

while (not condition)
    condvar.wait();

如果另一个线程想要唤醒等待线程,则必须将condition标志设置为true。 E.g:

condition = true;
condvar.notify_one();

我想知道,这种情况是否有可能阻止条件变量:

1)等待线程检查条件标志,发现它等于FALSE,因此,它将进入condvar.wait()例程。

2)但就在此之前(但在条件标志检查之后)等待线程被内核抢占(例如因为时隙到期)。

3)此时,另一个线程想要通知等待线程有关条件。它将条件标志设置为TRUE并调用condvar.notify_one();

4)当内核调度程序再次运行第一个线程时,它会进入condvar.wait()例程,但通知已经丢失。

因此,等待线程无法从condvar.wait()退出,尽管条件标志设置为TRUE,因为不再有唤醒通知。

有可能吗?

4 个答案:

答案 0 :(得分:15)

这正是为什么条件变量必须与互斥锁一起使用才能原子地更新状态并发出变化信号的原因。完整代码看起来更像:

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

和其他线程:

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

答案 1 :(得分:4)

您的示例缺少一小部分,但这解释了为什么如果正确完成则无法实现:

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

因此,如果在相同的互斥锁下将条件更改为true,则不会发生这种情况。

答案 2 :(得分:0)

迈克·西摩(Mike Seymour)的回答是不完整的,因为存在一种比赛条件,最终导致唤醒失败。 正确的方法是(现在使用c ++ 11)如下:

线程1:

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

线程2:

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

答案 3 :(得分:-2)

是的(我在2012年12月对此进行了测试),并且我刚才提出了一个解决方案。 “Flare”课程: 请注意,它使用自旋锁,但在此花费的时间很少。

宣言(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;

};

实施/定义(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();
}