6

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

while (not condition)
    condvar.wait();

如果另一个线程想要唤醒等待线程,它必须将条件标志设置为真。例如:

condition = true;
condvar.notify_one();

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

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

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

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

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

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

是否可以?

4

4 回答 4

16

这正是条件变量必须与互斥锁一起使用的原因,以便以原子方式更新状态并发出更改信号。完整的代码看起来更像:

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

对于另一个线程:

lock_guard<mutex> lock(mutex);
condition = true;
condvar.notify_one();
于 2013-02-25T17:25:35.507 回答
4

您的示例缺少一小部分,但这解释了为什么如果正确完成这是不可能的:

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

所以如果在同一个互斥锁下将condition改为true,就不会出现这种情况。

于 2013-02-25T17:21:29.083 回答
1

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();
于 2019-05-28T07:29:57.873 回答
-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();
}
于 2013-02-25T17:21:24.603 回答