Каковы общие применения переменных условий в С++?
Я пытаюсь узнать переменные условия. Я хотел бы знать, каковы обычные ситуации, когда используются переменные условия.
Один пример - в очереди блокировки, где два потока обращаются к очереди - поток производителя толкает элемент в очередь, а потребительский поток выталкивает элемент из очереди. Если очередь пуста, потребительский поток ожидает, пока сигнал будет отправлен потоком производителя.
Каковы другие проектные ситуации, когда вам нужна переменная условия, которая будет использоваться?
Я бы предпочел примеры, основанные на опыте, например, в реальных реальных приложениях.
Ответы
Ответ 1
Одно использование переменных условий, которые немного сложнее, чем просто очередь сообщений, заключается в "совместном использовании блокировки", где разные потоки ожидают тонко отличающихся условий одного и того же основного характера. Например, у вас есть (очень шонкий, упрощенный) веб-кеш. Каждая запись в кеше имеет три возможных состояния: нет, IN_PROGRESS, COMPLETE.
getURL:
lock the cache
three cases for the key:
not present:
add it (IN_PROGRESS)
release the lock
fetch the URL
take the lock
update to COMPLETE and store the data
broadcast the condition variable
goto COMPLETE
COMPLETE:
release the lock and return the data
IN_PROGRESS:
while (still IN_PROGRESS):
wait on the condition variable
goto COMPLETE
Я на практике использовал шаблон для реализации варианта функции POSIX pthread_once
без какой-либо помощи планировщика. Причина, по которой я не мог использовать семафор или блокировку на once_control
, и просто выполнить инициализацию под блокировкой, заключается в том, что функции не было отказано, а once_control
имела только тривиальную инициализацию. В этом случае pthread_once
сам по себе не имеет определенных кодов ошибок, поэтому его реализация с возможностью сбоя не оставляет вашего вызывающего абонента с любыми хорошими вариантами...
Конечно, с этим шаблоном вы должны быть осторожны в масштабировании. Каждый раз, когда всякая инициализация завершается, каждый ожидающий поток просыпается, чтобы захватить блокировку. Поэтому, когда вы разрабатываете систему, вы очень тщательно думаете о том, чтобы оштрафовать, а затем решите, что вам не нужно беспокоиться о том, чтобы что-то делать, пока вы не увидите доказанные проблемы с производительностью.
Ответ 2
В качестве примера, помимо модели потребителя-производителя, которую вы уже упоминали, используется в барьерной синхронизации. Когда потоки входят в барьер, если есть еще другие потоки, которые должны войти в барьер, тогда они ждут переменную условия. Последний поток для входа в барьер сигнализирует о состоянии.
Ответ 3
Я использовал его для отправки синхронизированных сообщений, где был добавлен объект синхронизации.
Объект sync состоял из переменной условия с "готовым" булевым.
В функции syncMsg:: send() была функция sync- > wait(), а в функции syncMsg:: handle() была синхронизация → go().
Должно использоваться разумно из-за возможных взаимоблокировок.
Ответ 4
Я использую переменные условия вместо подверженных ошибкам объектов событий Win32. С condvars вам не нужно так беспокоиться о ложной сигнализации. Кроме того, легче ждать появления нескольких событий.
Кондвары также могут заменить семафоры, потому что они более универсальны.
Ответ 5
Я знаю, что это не очень полезно, но я использую переменную условия в любое время, когда я хочу, чтобы поток ожидал чего-то, или только дождался, когда что-то случится.
Очень распространенный шаблон, в котором я использую переменную условия, представляет собой фоновый поток, который каждые несколько минут просыпается, чтобы выполнить некоторую обработку, а затем снова возвращается в режим сна. При выключении основного потока, чтобы сигнализировать фоновый поток, чтобы закончить, а затем присоедините его к завершению. Фоновый поток ожидает состояния с таймаутом, чтобы выполнить его сон.
Фоновый поток следует этой базовой логике
void threadFunction() {
initialisation();
while(! shutdown()) {
backgroundTask();
shutdown_condition_wait(timeout_value);
}
cleanup();
}
Это позволяет отключить фоновый поток быстро и изящно.
Если у меня есть ряд таких потоков, основная функция сигнализирует каждому об отключении, а затем присоединяется к каждому из них после следующего. Это позволяет параллельному отключению каждого компонента нити.