Ложное пробуждение с атомарными переменными и condition_variables

std::atomic<T> и std::condition_variable оба имеют функции-члены wait и notify_one. В некоторых приложениях у программистов может быть выбор между использованием любого из них для целей синхронизации. Одна из целей этих wait функций заключается в том, что они должны координировать свои действия с операционной системой, чтобы свести к минимуму ложные пробуждения. То есть операционная система должна избегать пробуждения wait-ing thread до тех пор, пока не будут вызваны notify_one или notify_all.

На моей машине sizeof(std::atomic<T>) — это sizeof(T), а sizeof(std::condition_variable) — это 72. Если вы исключите член std::atomic<T>T, то std::condition_variable резервирует 72 байта для целей синхронизации, а sizeof(std::atomic<T>) резервирует 0 байтов.

Мой вопрос: следует ли ожидать различного поведения между функциями std::condition_variable и std::atomic<T>wait? Например, должно ли std::condition_variable иметь меньше ложных пробуждений?

std::atomic<T>::wait()

std::atomic<T>::notify_one()

std::condition_variable::wait()

std::condition_variable::notify_one()

condition_variable будет брать мьютекс при каждом пробуждении, чтобы проверить условие ожидания, и освобождать его при возвращении в ожидание. Блокировка/освобождение мьютекса - это служебный вызов ОС (хорошо зависит от реализации, но обычно так и есть). Я ожидаю, что «атомарному» не понадобится такая защита для проверки состояния ожидания. Таким образом, «атомарному» вообще не нужно переключение потоков для получения мьютекса.
user3124812 11.05.2022 06:30

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

Mark Wallace 11.05.2022 07:08

что вы подразумеваете под «не проверяет, изменилась ли атомарная переменная»? Весь смысл ожидания в том, чтобы проверить, изменилась ли переменная

user3124812 11.05.2022 07:20

Вы сказали: «Поэтому 'atomic' вообще не требует переключения потоков для получения мьютекса». Я понял это так: «Вы можете подумать, что когда atomic проверяет, изменился ли его T, ваш процессор должен переключиться на вызывающий поток. Однако, поскольку «atomic не понадобится такая защита для проверки состояния ожидания», переключение потоков ненужно. Другое поток может проверить, изменились ли атомарные T, и если да, то разбудить вызывающий поток». Это имеет смысл для меня. Тогда я предполагаю, что какой-то процесс ОС отвечает за проверку того, изменились ли атомы T (для ВСЕХ атомов, ожидающих в настоящее время).

Mark Wallace 11.05.2022 07:36

Ага, неудачная формулировка... Тогда поправлюсь. cv.wait() освобождает мьютекс, когда засыпает, и снова блокирует его, когда просыпается из-за уведомления. Если мьютекс заблокирован другим потоком, поток, удерживающий cv, снова перейдет в спящий режим до тех пор, пока другой поток не освободит мьютекс. Как только cv получит мьютекс, он может проверить условие ожидания. Atomic не нуждается в мьютексе и потенциально может вызвать меньшее переключение контекста потока.

user3124812 11.05.2022 07:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
5
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

My question: should I expect different behavior between std::condition_variable's and std::atomic<T>'s wait functions? For example, should std::condition_variable have fewer spurious wakeups?

std::atomic::wait не имеет ложных пробуждений. Стандарт гарантирует, что было замечено измененное значение, в [atomics.types.generic.general]/30 написано:

Effects: Repeatedly performs the following steps, in order:

(30.1) Evaluates load(order) and compares its value representation for equality against that of old.

(30.2) If they compare unequal, returns.

(30.3) Blocks until it is unblocked by an atomic notifying operation or is unblocked spuriously.

Итак, если базовая реализация атомарного ожидания вызывает ложные пробуждения, они скрыты реализацией стандартной библиотеки C++..

Если ваши вопросы касаются того, есть ли больше или меньше ложных пробуждений в атомарных или условных переменных основная реализация, то это зависит от реализации. Будет зависеть от операционной системы и реализации библиотеки. Наиболее вероятный ответ: нет, потому что окончательная реализация, в которой ОС вызывает ядро, скорее всего, будет такой же.

Другие вопросы по теме