Я пытаюсь понять семантику std :: condition_variable . Я думал, что у меня было приличное понимание модели параллелизма C ++ 11 (атомика, упорядочение памяти, соответствующие гарантии и формальные отношения ), но описание того, как правильно использовать условные переменные, кажется, противоречит моему пониманию .
TL; DR
Ссылка говорит:
Поток, который намеревается изменить переменную, имеет значение
- получить std :: mutex (обычно через std :: lock_guard)
- выполнить модификацию, пока удерживается блокировка
- выполнить notify_one или notify_all для std :: condition_variable ( блокировку не нужно удерживать для уведомления)
Даже если общая переменная - это atomi c, ее необходимо изменить в мьютексе, чтобы правильно опубликовать sh изменение ожидающего нить.
Я могу понять, почему модификацию, возможно, нужно сделать перед освобождением мьютекса, но вышеизложенное кажется достаточно ясным, что это должно быть пока удерживает мьютекс, то есть он не может быть до его получения . Я правильно читаю это?
Более подробно
Если мое прочтение вышеизложенного верно, то почему это так? Представьте, что мы выполняем модификации перед критической секцией (не допуская гонки, путем правильного использования атомных и блокировок). Например,
std::atomic<bool> dummy;
std::mutex mtx;
std::condition_variable cv;
void thread1() {
//...
// Modify some program data, possibly in many places, over a long period of time
dummy.store(true, std::memory_order_relaxed); // for simplicity
//...
mtx.lock(); mtx.unlock();
cv.notify_one();
//...
}
void thread2() {
// ...
{ std::unique_lock<std::mutex> ul(mtx);
cv.wait(ul, []() -> bool {
// A complex condition, possibly involving data from many places
return dummy.load(std::memory_order_relaxed); // for simplicity
});
}
// ...
}
Насколько я понимаю, cv.wait()
блокируется на mtx
перед продолжением (чтобы проверить условие и выполнить остальную часть программы). Кроме того, std::mutex::lock()
считается операцией acqu , а std::mutex::unlock()
считается операцией release . Не означает ли это, что unlock () в thread1 синхронизируется с lock () в thread2, и, следовательно, все хранилища atomi c и даже не Atomi c выполняются в thread1 до unlock()
видны для thread2, когда он просыпается?
Formally: store --sequenced-before--> unlock() --synchronizes-with--> lock() --sequenced-before--> load
...and so: store --happens-before--> load
Большое спасибо за любые ответы!
[Примечание: я нахожу странным, что я не нашел ответа на этот вопрос после обширного прибегая к помощи; Извините, если это дубликат ...]