std::condition_variable — cppreference.com
Материал из cppreference.com
<tbody> </tbody>
Класс condition_variable это примитив синхронизации, который можно использовать для блокировки потока или нескольких потоков одновременно, пока другой поток не изменит совместно используемую переменную (условие) и не уведомит condition_variable.
Поток, который намеревается изменить общую переменную, должен
- получить
std::mutex(обычно через std::lock_guard) - выполнить модификацию, пока блокировка удерживается
- выполнить notify_one или notify_all для
std::condition_variable(блокировку не требуется удерживать для уведомления)
Даже если общая переменная является атомарной, она должна быть изменена в мьютексе, чтобы правильно опубликовать изменение в ожидающем потоке.
Любой поток, который намеревается ожидать std::condition_variable, должен
- получить
std::unique_lock<std::mutex>на том же мьютексе, который используется для защиты общей переменной - либо
- проверить состояние, если оно уже было обновлено и уведомлено
- выполнить wait, wait_for или wait_until. Операции ожидания атомарно освобождают мьютекс и приостанавливают выполнение потока.
- Когда условная переменная уведомляется, истекает тайм-аут или происходит ложное пробуждение, поток пробуждается, и мьютекс повторно запрашивается атомарно. Затем поток должен проверить условие и возобновить ожидание, если пробуждение было ложным.
- или
- используйте предикативную перегрузку wait, wait_for и wait_until, которая заботится о трёх шагах, описанных выше
std::condition_variable работает только с std::unique_lock<std::mutex>; это ограничение позволяет добиться максимальной эффективности на некоторых платформах. std::condition_variable_any предоставляет условную переменную, которая работает с любым объектом BasicLockable, например std::shared_lock.
Условные переменные разрешают одновременный вызов функций-элементов wait, wait_for, wait_until, notify_one и notify_all.
Класс std::condition_variable это StandardLayoutType. Он не является CopyConstructible, MoveConstructible, CopyAssignable или MoveAssignable.
Типы-элементы
| Тип-элемент | Определение |
native_handle_type
|
определено реализацией |
Функции-элементы
| создаёт объект (public функция-элемент) [править] | |
| разрушает объект (public функция-элемент) [править] | |
operator= [удалено] |
без копирования присваиванием (public функция-элемент) [править] |
Уведомление | |
| уведомляет один ожидающий поток (public функция-элемент) [править] | |
| уведомляет все ожидающие потоки (public функция-элемент) [править] | |
Одидание | |
| блокирует текущий поток до тех пор, пока условная переменная не будет активирована (public функция-элемент) [править] | |
| блокирует текущий поток до тех пор, пока условная переменная не будет активирована или по истечении указанного времени ожидания (public функция-элемент) [править] | |
| блокирует текущий поток до тех пор, пока условная переменная не будет активирована или пока не будет достигнут указанный момент времени (public функция-элемент) [править] | |
Встроенный дескриптор | |
| возвращается системный дескриптор (public функция-элемент) [править] | |
Пример
condition_variable используется в сочетании с std::mutex для облегчения взаимодействия между потоками.
#include <iostream> #include <string> #include <thread> #include <mutex> #include <condition_variable> std::mutex m; std::condition_variable cv; std::string data; bool ready = false; bool processed = false; void worker_thread() { // Ждёт, пока main() отправит данные std::unique_lock lk(m); cv.wait(lk, []{return ready;}); // после ожидания мы владеем блокировкой. std::cout << "Поток worker обрабатывает данные\n"; data += " после обработки"; // Отправляет данные обратно в main() processed = true; std::cout << "Поток worker сигнализирует о завершении обработки данных\n"; // Ручная разблокировка выполняется перед уведомлением, чтобы не разбудить // ожидающий поток только для повторной блокировки (подробности смотрите в notify_one) lk.unlock(); cv.notify_one(); } int main() { std::thread worker(worker_thread); data = "Пример данных"; // отправляет данные в поток worker { std::lock_guard lk(m); ready = true; std::cout << "main() сигнализирует о готовности данных к обработке\n"; } cv.notify_one(); // ожидание worker { std::unique_lock lk(m); cv.wait(lk, []{return processed;}); } std::cout << "Возвращение в main(), data = " << data << '\n'; worker.join(); }
Вывод:
main() сигнализирует о готовности данных к обработке Поток worker обрабатывает данные Поток worker сигнализирует о завершении обработки данных Возвращение в main(), data = Пример данных после обработки