D.2.1. Класс std::condition_variable

Класс std::condition_variable позволяет потоку ждать выполнения условия.

Экземпляры этого класса не удовлетворяют концепциям CopyAssignable, CopyConstructible, MoveAssignable, MoveConstructible.

Определение класса

class condition_variable {

public:

 condition_variable();

 ~condition_variable();

 condition_variable(condition_variable const&) = delete;

 condition_variable& operator=(

  condition_variable const&) = delete;

 void notify_one() noexcept;

 void notify_all() noexcept;

 void wait(std::unique_lock<std::mutex>& lock);

 template <typename Predicate>

 void wait(std::unique_lock<std::mutex>& lock, Predicate pred);

 template <typename Clock, typename Duration>

 cv_status wait_until(

  std::unique_lock<std::mutex>& lock,

  const std::chrono::time_point<Clock, Duration>& absolute_time);

 template <typename Clock, typename Duration, typename Predicate>

 bool wait_until(

  std::unique_lock<std::mutex>& lock,

  const std::chrono::time_point<Clock, Duration>& absolute_time,

  Predicate pred);

 template <typename Rep, typename Period>

 cv_status wait_for(

  std::unique_lock<std::mutex>& lock,

  const std::chrono::duration<Rep, Period>& relative_time);

 template <typename Rep, typename Period, typename Predicate>

 bool wait_for(

  std::unique_lock<std::mutex>& lock,

  const std::chrono::duration<Rep, Period>& relative_time,

  Predicate pred);

};

void notify_all_at_thread_exit(

 condition_variable&, unique_lock<mutex>);

STD::CONDITION_VARIABLE , КОНСТРУКТОР ПО УМОЛЧАНИЮ

Конструирует объект типа std::condition_variable.

Объявление

condition_variable();

Результат

Конструирует объект типа std::condition_variable.

Исключения

Исключение типа std::system_error, если сконструировать условную переменную не получилось.

STD::CONDITION_VARIABLE , ДЕСТРУКТОР

Уничтожает объект std::condition_variable.

Объявление

~condition_variable();

Предусловия

Не существует потоков, заблокированных по *this в обращениях к wait(), wait_for() или wait_until().

Результат

Уничтожает *this.

Исключения

Нет.

STD::CONDITION_VARIABLE::NOTIFY_ONE , ФУНКЦИЯ-ЧЛЕН

Пробуждает один из потоков, ожидающих std::condition_variable.

Объявление

void notify_one() noexcept;

Результат

Пробуждает один из потоков, ожидающих *this, в точке вызова. Если таких потоков нет, функция не имеет никакого эффекта.

Исключения

Исключение типа std::system_error, если действие не выполнено.

Синхронизация

Обращения к функциям notify_one(), notify_all(), wait(), wait_for() и wait_until() одного и того же объекта std::condition_variable сериализуются. Обращение к notify_one() или notify_all() будит только потоки, запущенные до этого обращения.

STD::CONDITION_VARIABLE::NOTIFY_ALL , ФУНКЦИЯ-ЧЛЕН

Пробуждает все потоки, ожидающие std::condition_variable.

Объявление

void notify_all() noexcept;

Результат

Пробуждает все потоки, ожидающие *this, в точке вызова. Если таких потоков нет, функция не имеет никакого эффекта.

Исключения

Исключение типа std::system_error, если действие не выполнено.

Синхронизация

Обращения к функциям notify_one(), notify_all(), wait(), wait_for() и wait_until() одного и того же объекта std::condition_variable сериализуются. Обращение к notify_one() или notify_all() будит только потоки, запущенные до этого обращения.

STD::CONDITION_VARIABLE::WAIT , ФУНКЦИЯ-ЧЛЕН

Ожидает, пока условная переменная std::condition_variable не получит сигнал в результате обращения к notify_one() или notify_all() либо не произойдёт ложное пробуждение.

Объявление

void wait(std::unique_lock<std::mutex>& lock);

Предусловия

Значение lock.owns_lock() равно true, и блокировкой владеет вызывающий поток.

Результат

Атомарно разблокирует предоставленный объект lock и блокирует поток, пока он не будет разбужен обращением к notify_one() или notify_all() из другого потока либо не произойдёт ложное пробуждение. Перед возвратом управления из wait() объект lock снова блокируется.

Исключения

Исключение типа std::system_error, если действие не выполнено. Если объект lock был разблокирован при обращении к wait(), он снова блокируется при выходе из нее, даже если выход произошёл в результате исключения.

Примечание. Ложное пробуждение означает, что поток, вызвавший wait(), может быть разбужен, даже если ни один другой поток не обращался к notify_one() или notify_all(). Поэтому рекомендуется использовать перегруженный вариант wait(), который принимает предикат. Если это нежелательно, то рекомендуется вызывать wait() в цикле, где проверяется предикат, ассоциированный с условной переменной.

Синхронизация

Обращения к функциям notify_one(), notify_all(), wait(), wait_for() и wait_until() одного и того же объекта std::condition_variable сериализуются. Обращение к notify_one() или notify_all() будит только потоки, запущенные до этого обращения.

STD::CONDITION_VARIABLE::WAIT , ПЕРЕГРУЖЕННАЯ ФУНКЦИЯ-ЧЛЕН, ПРИНИМАЮЩАЯ ПРЕДИКАТ

Ожидает, пока условная переменная std::condition_variable не получит сигнал в результате обращения к notify_one() или notify_all() и при этом предикат равен true.

Объявление

template<typename Predicate>

void wait(std::unique_lock<std::mutex>& lock, Predicate pred);

Предусловия

Выражение pred() должно быть допустимо и возвращать значение, преобразуемое в тип bool. Значение lock.owns_lock() должно быть равно true, и владельцем блокировки lock должен быть поток, вызвавший wait().

Результат

Эквивалентно циклу

while (!pred()) {

 wait(lock);

}

Исключения

Исключение, возбужденное в результате обращения к pred, или std::system_error, если действие не выполнено.

Примечание. Возможность ложного пробуждения означает, что функция pred может вызываться несколько раз (сколько именно, не определено). При любом вызове pred мьютекс, на который ссылается объект lock, гарантированно будет захвачен, и функция вернет управление тогда и только тогда, когда результатом вычисления (bool)pred() является true.

Синхронизация

Обращения к функциям notify_one(), notify_all(), wait(), wait_for() и wait_until() одного и того же объекта std::condition_variable сериализуются. Обращение к notify_one() или notify_all() будит только потоки, запущенные до этого обращения.

STD::CONDITION_VARIABLE::WAIT_FOR , ФУНКЦИЯ-ЧЛЕН

Ожидает, пока условная переменная std::condition_variable не получит сигнал в результате обращения к notify_one() или notify_all(), либо не истечет таймаут, либо не произойдёт ложное пробуждение.

Объявление

template<typename Rep, typename Period>

cv_status wait_for(

std::unique_lock<std::mutex>& lock,

std::chrono::duration<Rep, Period> const& relative_time);

Предусловия

Значение lock.owns_lock() равно true, и блокировкой владеет вызывающий поток.

Результат

Атомарно разблокирует предоставленный объект lock и блокирует поток, пока он не будет разбужен обращением к notify_one() или notify_all() из другого потока, либо не истечет таймаут, заданный аргументом relative_time, либо не произойдёт ложное пробуждение. Перед возвратом управления из wait_for() объект lock снова блокируется.

Возвращаемое значение

std::cv_status::no_timeout, если поток был разбужен в результате обращения к notify_one() или notify_all() либо ложного пробуждения. В противном случае std::cv_status::timeout.

Исключения

Исключение типа std::system_error, если действие не выполнено. Если объект lock был разблокирован при обращении к wait_for(), он снова блокируется при выходе из нее, даже если выход произошёл в результате исключения.

Примечание. Ложное пробуждение означает, что поток, вызвавший wait_for(), может быть разбужен, даже если ни один другой поток не обращался к notify_one() или notify_all(). Поэтому рекомендуется использовать перегруженный вариант wait_for(), который принимает предикат. Если это нежелательно, то рекомендуется вызывать wait_for() в цикле, где проверяется предикат, ассоциированный с условной переменной. При этом необходимо следить, не истек ли таймаут. Во многих случаях предпочтительнее использовать функцию wait_until(). Поток может быть блокирован дольше, чем указано. Если возможно, истекшее время измеряется по стабильным часам.

Синхронизация

Обращения к функциям notify_one(), notify_all(), wait(), wait_for() и wait_until() одного и того же объекта std::condition_variable сериализуются. Обращение к notify_one() или notify_all() будит только потоки, запущенные до этого обращения.

STD::CONDITION_VARIABLE::WAIT_FOR , ПЕРЕГРУЖЕННАЯ ФУНКЦИЯ-ЧЛЕН, ПРИНИМАЮЩАЯ ПРЕДИКАТ

Ожидает, пока условная переменная std::condition_variable не получит сигнал в результате обращения к notify_one() или notify_all() и при этом предикат равен true, либо не истечет указанный таймаут.

Объявление

template<typename Rep, typename Period, typename Predicate>

bool wait_for(

 std::unique_lock<std::mutex>& lock,

 std::chrono::duration<Rep, Period> const& relative_time,

 Predicate pred);

Предусловия

Выражение pred() должно быть допустимо и возвращать значение, преобразуемое в тип bool. Значение lock.owns_lock() должно быть равно true, и владельцем блокировки lock должен быть поток, вызвавший wait_for().

Результат

Эквивалентно следующему коду:

internal_clock::time_point end =

 internal_clock::now() + relative_time;

while (!pred()) {

 std::chrono::duration<Rep, Period> remaining_time =

  end-internal_clock::now();

 if (wait_for(lock, remaining_time) == std::cv_status::timeout)

  return pred();

}

return true;

Возвращаемое значение

true, если последнее обращение к pred() вернуло true; false, если истекло время, заданное в аргументе relative_time и обращение к pred() вернуло false.

Примечание. Возможность ложного пробуждения означает, что функция pred может вызываться несколько раз (сколько именно, не определено). При любом вызове pred мьютекс, на который ссылается объект lock, гарантированно будет захвачен, и функция вернет управление тогда и только тогда, когда результатом вычисления (bool)pred() является true или истекло время, заданное в аргументе relative_time. Поток может быть блокирован дольше, чем указано. Если возможно, истекшее время измеряется по стабильным часам.

Исключения

Исключение, возбужденное в результате обращения к pred, или std::system_error, если действие не выполнено.

Синхронизация

Обращения к функциям notify_one(), notify_all(), wait(), wait_for() и wait_until() одного и того же объекта std::condition_variable сериализуются. Обращение к notify_one() или notify_all() будит только потоки, запущенные до этого обращения.

STD::CONDITION_VARIABLE::WAIT_UNTIL , ФУНКЦИЯ-ЧЛЕН

Ожидает, пока условная переменная std::condition_variable не получит сигнал в результате обращения к notify_one() или notify_all() либо не будет достигнут указанный момент времени, либо не произойдёт ложное пробуждение.

Объявление

template<typename Clock, typename Duration>

cv_status wait_until(

 std::unique_lock<std::mutex>& lock,

 std::chrono::time_point<Clock, Duration> const& absolute_time);

Предусловия

Значение lock.owns_lock() равно true, и владельцем блокировки lock является вызывающий поток.

Результат

Атомарно разблокирует предоставленный объект lock и блокирует поток, пока он не будет разбужен обращением к notify_one() или notify_all() из другого потока, либо функция Clock::now() не вернет время, большее или равное absolute_time, либо не произойдёт ложное пробуждение. Перед возвратом управления из wait_until() объект lock снова блокируется.

Возвращаемое значение

std::cv_status::no_timeout, если поток был разбужен в результате обращения к notify_one() или notify_all() либо ложного пробуждения. В противном случае std::cv_status::timeout.

Исключения

Исключение типа std::system_error, если действие не выполнено. Если объект lock был разблокирован при обращении к wait_for(), он снова блокируется при выходе из нее, даже если выход произошёл в результате исключения.

Примечание. Ложное пробуждение означает, что поток, вызвавший wait_until(), может быть разбужен, даже если ни один другой поток не обращался к notify_one() или notify_all(). Поэтому рекомендуется использовать перегруженный вариант wait_until(), который принимает предикат. Если это нежелательно, то рекомендуется вызывать wait_until() в цикле, где проверяется предикат, ассоциированный с условной переменной. Не дается никаких гарантий относительно того, сколько времени будет блокирован вызывающий поток. Гарантируется лишь, что если функция вернула false, то значение, возвращенное Clock::now(), больше или равно absolute_time в точке, где поток разблокировался.

Синхронизация

Обращения к функциям notify_one(), notify_all(), wait(), wait_for() и wait_until() одного и того же объекта std::condition_variable сериализуются. Обращение к notify_one() или notify_all() будит только потоки, запущенные до этого обращения.

STD::CONDITION_VARIABLE::WAIT_UNTIL , ПЕРЕГРУЖЕННАЯ ФУНКЦИЯ-ЧЛЕН, ПРИНИМАЮЩАЯ ПРЕДИКАТ

Ожидает, пока условная переменная std::condition_variable не получит сигнал в результате обращения к notify_one() или notify_all(), и при этом предикат равен true, либо не будет достигнут указанный момент времени.

Объявление

template<typename Clock, typename Duration, typename Predicate>

bool wait_until(

 std::unique_lock<std::mutex>& lock,

 std::chrono::time_point<Clock, Duration> const& absolute_time,

 Predicate pred);

Предусловия

Выражение pred() должно быть допустимо и возвращать значение, преобразуемое в тип bool. Значение lock.owns_lock() должно быть равно true, и владельцем блокировки lock должен быть поток, вызвавший wait_until().

Результат

Эквивалентно следующему коду:

while (!pred()) {

 if (wait_until(lock, absolute_time) == std::cv_status::timeout)

  return pred();

}

return true;

Возвращаемое значение

true, если последнее обращение к pred() вернуло true; false, если функция Clock::now() вернула время, большее или равное absolute_time, и обращение к pred() вернуло false.

Примечание. Возможность ложного пробуждения означает, что функция pred может вызываться несколько раз (сколько именно, не определено). При любом вызове pred мьютекс, на который ссылается объект lock, гарантированно будет захвачен, и функция вернет управление тогда и только тогда, когда результатом вычисления (bool)pred() является true или функция Clock::now() вернула время, больше или равное absolute_time. Не дается никаких гарантий относительно того, сколько времени будет блокирован вызывающий поток. Гарантируется лишь, что если функция вернула false, то значение, возвращенное Clock::now(), больше или равно absolute_time в точке, где поток разблокировался.

Исключения

Исключение, возбужденное в результате обращения к pred, или std::system_error, если действие не выполнено.

Синхронизация

Обращения к функциям notify_one(), notify_all(), wait(), wait_for() и wait_until() одного и того же объекта std::condition_variable сериализуются. Обращение к notify_one() или notify_all() будит только потоки, запущенные до этого обращения.

STD::NOTIFY_ALL_AT_THREAD_EXIT , ФУНКЦИЯ, НЕ ЯВЛЯЮЩАЯСЯ ЧЛЕНОМ КЛАССА

Пробуждает все потоки, ожидающие std::condition_variable, при завершении текущего потока.

Объявление

void notify_all_at_thread_exit(

 condition_variable& cv, unique_lock<mutex> lk);

Предусловия

Значение lock.owns_lock() равно true, и владельцем блокировки lock является вызывающий поток. Функция lk.mutex() должна возвращать такое же значение, как для любого объекта блокировки, передаваемого функциям-членам wait(), wait_for() или wait_until() объекта cv из одновременно ожидающих потоков.

Результат

Передает владение мьютексом, захваченным lk, внутреннему объекту и планирует отправку уведомления условной переменной cv при завершении вызывающего потока. Уведомление эквивалентно выполнению следующего кода:

lk.unlock();

cv.notify_all();

Исключения

Возбуждает исключение std::system_error, если действие не выполнено.

Примечание. Блокировка удерживается до завершения потока, поэтому необходимо предпринимать меры для предотвращения взаимоблокировки. Рекомендуется завершать вызывающий поток как можно раньше и не выполнять в нем никаких блокирующих операций.

Пользователь должен следить за тем, чтобы ожидающий поток не сделал ошибочного предположения о том, что в момент его пробуждения данный поток уже завершен, — в частности, из-за возможности ложного пробуждения. Для этого можно проверять в ожидающем потоке предикат, который может быть сделан истинным только уведомляющим потоком, причём это должно делаться под защитой мьютекса, который не освобождается до вызова notify_all_at_thread_exit.