Рассмотрим следующую цитату о std::condition_variable
из cppreference:
Класс
condition_variable
— это примитив синхронизации, который можно использовать для блокировки потока или нескольких потоков одновременно, пока другой поток не изменит общую переменную (условие). , и уведомляетcondition_variable
.Поток, намеревающийся изменить переменную, должен
- получить
std::mutex
(обычно черезstd::lock_guard
)- выполнить изменение, пока блокировка удерживается
- выполнить
notify_one
илиnotify_all
наstd::condition_variable
(блокировку не нужно удерживать для уведомления)Даже если общая переменная является атомарной, ее необходимо изменить в мьютексе, чтобы правильно опубликовать изменение в ожидающем потоке.
Примерный типичный сценарий этого подхода выглядит следующим образом:
// shared (e.d., global) variables:
bool proceed = false;
std::mutex m;
std::condition_variable cv;
// thread #1:
{
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l); // or, cv.wait(l, []{ return proceed; });
}
// thread #2:
{
std::lock_guard<std::mutex> l(m);
proceed = true;
}
cv.notify_one();
Однако @Tim в этом вопросе предложил (своего рода академическую) альтернативу:
std::atomic<bool> proceed {false}; // atomic to avoid data race
std::mutex m;
std::condition_variable cv;
// thread #1:
{
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);
}
// thread #2:
proceed = true; // not protected with mutex
{ std::lock_guard<std::mutex> l(m); }
cv.notify_one();
Очевидно, что это не соответствует требованиям приведенной выше цитаты cppreference, поскольку общая переменная proceed
(атомарная в данном случае) не изменяется в мьютексе.
Вопрос в том, правильный ли этот код. На мой взгляд, да, поскольку:
Первый вариант заключается в том, что
!proceed
вwhile (!proceed)
оценивается как false. В этом случаеcv.wait(l);
вообще не вызывается.Второй вариант заключается в том, что
!proceed
вwhile (!proceed)
оценивается как истина. В этом случаеcv.notify_one()
не может произойти, пока поток №1 не войдет вcv.wait(l);
.
Я что-то упустил или cppreference неправильный в этом отношении? (Например, связаны ли какие-то проблемы с изменением порядка?)
А что, если proceed = true;
изменить на proceed.store(true, std::memory_order_relaxed);
?