Нужно ли модифицировать разделяемую переменную, относящуюся к условной переменной под мьютексом? - PullRequest
1 голос
/ 23 апреля 2020

Рассмотрим следующую цитату о std::condition_variable из cppreference :

Класс condition_variable является примитивом синхронизации, который можно использовать для блокировки потока или нескольких потоков. в то же время, пока другой поток не изменит общую переменную (условие ) и не уведомит condition_variable.

поток, который намеревается изменить переменная должна

  1. получить std::mutex (обычно через std::lock_guard)
  2. выполнить модификацию, пока удерживается блокировка
  3. выполнить notify_one или notify_all в std::condition_variable (блокировка не требуется для уведомления)

Даже если общая переменная - atomi c, она должна быть измененным под мьютексом , чтобы правильно опубликовать sh модификацию ожидающего потока.

Примерный типичный сценарий этого подхода следующий:

// 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 в этот вопрос придумал (добрый - альтернативы Academi c):

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 общая переменная (atomi c в этом case) не изменяется под мьютексом .

Вопрос в том, верен ли этот код. На мой взгляд, так и есть, поскольку:

  1. Первый вариант - !proceed в while (!proceed) оценивается как false . В этом случае cv.wait(l); вообще не вызывается.

  2. Второй вариант заключается в том, что !proceed в while (!proceed) оценивается как true . В этом случае cv.notify_one() не может произойти до тех пор, пока поток # 1 не войдет в cv.wait(l);.

Я что-то упустил или неверная ссылка в этом отношении? (Для Например, есть ли какие-то проблемы с переупорядочением?)


И что, если proceed = true; будет изменен на proceed.store(true, std::memory_order_relaxed);?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...