std :: condition_variable ложная блокировка

Как вы знаете, условные переменные должны вызываться в цикле, чтобы избежать ложных пробуждений. Как это:

while (not condition)
condvar.wait();

Если другой поток хочет разбудить ожидающий поток, он должен установить для флага условия значение true. Например.:

condition = true;
condvar.notify_one();

Интересно, возможно ли заблокировать переменную условия по этому сценарию:

1) Ожидающий поток проверяет флаг условия и находит его равным FALSE, поэтому он собирается ввести condvar.wait() рутина.

2) Но непосредственно перед этим (но после проверки флага условия) ожидающий поток прерывается ядром (например, из-за истечения временного интервала).

3) В это время другой поток хочет уведомить ожидающий поток о состоянии. Устанавливает флаг условия в TRUE и вызывает condvar.notify_one();

4) Когда планировщик ядра снова запускает первый поток, он входит condvar.wait() рутина, но уведомление уже пропущено.

Таким образом, ожидающий поток не может выйти из condvar.wait(), несмотря на то, что флаг условия установлен в TRUE, потому что больше нет уведомлений о пробуждении.

Является ли это возможным?

6

Решение

Именно поэтому условная переменная должна использоваться вместе с мьютексом, чтобы атомарно обновлять состояние и сигнализировать об изменении. Полный код будет выглядеть так:

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

а для другой темы:

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

Другие решения

В вашем примере отсутствует небольшая часть, но это объясняет, почему это невозможно, если все сделано правильно:

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

Поэтому, если вы измените условие на true при той же блокировке мьютекса, такой ситуации не произойдет.

4

Да (я проверял это в декабре 2012 года), и есть решение, которое я придумал для этого некоторое время назад. Класс «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();
}
-2