std::condition_variable
| Определено в заголовочном файле <condition_variable>
|
||
class condition_variable; |
(начиная с C++11) | |
Класс 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 = Пример данных после обработки
Смотрите также
(C++11) |
предоставляет условную переменную, связанную с любым типом блокировки (класс) |
(C++11) |
обеспечивает базовую функциональность взаимного исключения (класс) |
(C++11) |
реализует обёртку владения мьютексом строго в области видимости (шаблон класса) |
(C++11) |
реализует перемещаемую оболочку владения мьютексом (шаблон класса) |