Domanda

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. E.g.:

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.

2)But just before this (but after condition flag checking) waiting thread is preempted by kernel (e.g. because of time slot expiration).

3) At this time, another thread wants to notify waiting thread about condition. It sets condition flag to TRUE and calls condvar.notify_one();

4) When kernel scheduler runs first thread again, it enters condvar.wait() routine, but the notification have been already missed.

So, waiting thread can't exit from condvar.wait(), despite condition flag is set to TRUE, because there is no wake up notifications anymore.

Is it possible?

È stato utile?

Soluzione

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

Altri suggerimenti

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.

Mike Seymour his answer is incomplete because there is a race condition which ends up with wakeup lost. The right way is to (now with the c++11) is as follow:

Thread1:

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

Thread2:

{
    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. The "Flare" class: Note that it uses a spin lock, but the time spent in this is minimal.

Declaration (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):

#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();
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top