33 Thread support library [thread]

33.5 Condition variables [thread.condition]

Переменные условия предоставляют примитивы синхронизации, используемые для блокировки потока до тех пор, пока какой-либо другой поток не получит уведомление о том, что какое-то условие выполнено, или пока не будет достигнуто системное время. Классcondition_­variable предоставляет переменную условия, которая может ожидать только объекта типаunique_­lock<mutex>, что обеспечивает максимальную эффективность на некоторых платформах. Классcondition_­variable_­any предоставляет переменную общего условия, которая может ожидать объектов типов блокировки, задаваемых пользователем.

Переменные условия позволяют одновременное призываниеwait,wait_­for, wait_­until,notify_­one иnotify_­all функций - членов.

Выполнениеnotify_­one иnotify_­all должно быть атомарным. Исполнениеwait,wait_­forиwait_­until должно быть выполнено в течение трех атомных частей:

  1. 1.освобождение мьютекса и переход в состояние ожидания;

  2. 2.разблокировка ожидания; а также

  3. 3.повторное приобретение замка.

Реализация должна вести себя так , как будто все расстрелыnotify_­one,notify_­allи каждую частьwait,wait_­forиwait_­until казни исполняются в одном неопределенном общем порядке в соответствии с ним «происходит до» порядка.

Не требуется синхронизировать создание и уничтожение переменных состояния.

33.5.1 Header <condition_­variable> synopsis [condition_variable.syn]

namespace std {
  class condition_variable;
  class condition_variable_any;

  void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);

  enum class cv_status { no_timeout, timeout };
}

33.5.2 Non-member functions [thread.condition.nonmember]

void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);

Requires:lk заблокирован вызывающим потоком и либо

  • никакой другой поток не ждетcond, или

  • lk.mutex() возвращает то же значение для каждого из аргументов блокировки поставляемых все одновременно ожидания ( с помощьюwait,wait_­for, илиwait_­until) нитей.

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

lk.unlock();
cond.notify_all();

Synchronization: Подразумеваемыйlk.unlock() вызов упорядочивается после уничтожения всех объектов с продолжительностью хранения потока, связанной с текущим потоком.

[ Note: Поставляемая блокировка будет удерживаться до тех пор, пока поток не завершится, и необходимо следить за тем, чтобы это не привело к возникновению тупиковой ситуации из-за проблем с упорядочением блокировок. После вызоваnotify_­all_­at_­thread_­exit рекомендуется как можно скорее выйти из потока и чтобы в этом потоке не выполнялись блокирующие или трудоемкие задачи. ]end note

[ Note: Пользователь несет ответственность за то, чтобы ожидающие потоки не ошибочно предполагали, что поток завершился, если они испытывают ложное пробуждение. Обычно это требует, чтобы ожидаемое условие удовлетворялось при удержании блокировкиlk, и чтобы эта блокировка не снималась и не запрашивалась повторно перед вызовомnotify_­all_­at_­thread_­exit. ] end note

33.5.3 Class condition_­variable [thread.condition.condvar]

namespace std {
  class condition_variable {
  public:

    condition_variable();
    ~condition_variable();

    condition_variable(const condition_variable&) = delete;
    condition_variable& operator=(const condition_variable&) = delete;

    void notify_one() noexcept;
    void notify_all() noexcept;
    void wait(unique_lock<mutex>& lock);
    template <class Predicate>
      void wait(unique_lock<mutex>& lock, Predicate pred);
    template <class Clock, class Duration>
      cv_status wait_until(unique_lock<mutex>& lock,
                           const chrono::time_point<Clock, Duration>& abs_time);
    template <class Clock, class Duration, class Predicate>
      bool wait_until(unique_lock<mutex>& lock,
                      const chrono::time_point<Clock, Duration>& abs_time,
                      Predicate pred);

    template <class Rep, class Period>
      cv_status wait_for(unique_lock<mutex>& lock,
                         const chrono::duration<Rep, Period>& rel_time);
    template <class Rep, class Period, class Predicate>
      bool wait_for(unique_lock<mutex>& lock,
                    const chrono::duration<Rep, Period>& rel_time,
                    Predicate pred);

    using native_handle_type = implementation-defined; // See [thread.req.native]
    native_handle_type native_handle();                // See [thread.req.native]
  };
}

Классcondition_­variable должен быть классом стандартной компоновки (пункт[class]).

condition_variable();

Effects: Создает объект типаcondition_­variable.

Throws:system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • resource_­unavailable_­try_­again - если какое-то ограничение ресурсов, не связанных с памятью, препятствует инициализации.

~condition_variable();

Requires: Не должно быть блокировок резьбы*this. [ Note: То есть все потоки должны быть уведомлены; впоследствии они могут заблокировать блокировку, указанную в ожидании. Это ослабляет обычные правила, которые требовали бы, чтобы все вызовы ожидания выполнялись до уничтожения. Только уведомление для разблокировки ожидания должно произойти до уничтожения. Пользователь должен позаботиться о том, что ни один из потоков не ждать*this когда был запущен деструктор, особенно когда ожидающие потоки вызова функции ожидания в цикле или с использованием перегрузокwait,wait_­forилиwait_­until что взять предикат. ]end note

Effects: Разрушает объект.

void notify_one() noexcept;

Effects: Если какие-либо потоки заблокированы в ожидании*this, разблокирует один из этих потоков.

void notify_all() noexcept;

Effects: Разблокирует все потоки, которые заблокированы в ожидании*this.

void wait(unique_lock<mutex>& lock);

Requires:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком, и либо

  • никакой другой поток не ожидает этогоcondition_­variable объекта или

  • lock.mutex() возвращает то же значение для каждого изlock аргументов , поставляемых всех одновременно ожидания ( с помощьюwait, wait_­forилиwait_­untilнитей).

Effects:

  • Атомарно звонитlock.unlock() и блокируется*this.

  • При разблокировке вызываетlock.lock() (возможно, блокировку по блокировке), затем возвращается.

  • Функция разблокируется при получении сигнала отnotify_­one() вызоваnotify_­all(), вызова или ложного сигнала .

Remarks: Если функция не удовлетворяет постусловию,terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ]end note

Postconditions:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком.

Throws: Ничего такого.

template <class Predicate> void wait(unique_lock<mutex>& lock, Predicate pred);

Requires:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком, и либо

  • никакой другой поток не ожидает этогоcondition_­variable объекта или

  • lock.mutex() возвращает то же значение для каждого изlock аргументов , поставляемых всех одновременно ожидания ( с помощьюwait, wait_­forилиwait_­untilнитей).

Effects: Эквивалентен:

while (!pred())
  wait(lock);

Remarks: Если функция не удовлетворяет постусловию,terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ]end note

Postconditions:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком.

Throws: Любое выброшенное исключениеpred.

template <class Clock, class Duration> cv_status wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time);

Requires:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком, и либо

  • никакой другой поток не ожидает этогоcondition_­variable объекта или

  • lock.mutex() возвращает то же значение для каждого изlock аргументов , поставляемых всех одновременно ожидания ( с помощьюwait, wait_­forилиwait_­untilнитей).

Effects:

  • Атомарно звонитlock.unlock() и блокируется*this.

  • При разблокировке вызываетlock.lock() (возможно, блокировку по блокировке), затем возвращается.

  • Функция разблокируется при получении сигнала от вызоваnotify_­one(), вызоваnotify_­all(), истечения абсолютного тайм-аута ([thread.req.timing]), указанного с помощьюabs_­time, или ложно.

  • Если функция завершается через исключение, онаlock.lock() должна вызываться до выхода из функции.

Remarks: Если функция не удовлетворяет постусловию,terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ]end note

Postconditions:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком.

Returns:cv_­status​::​timeout если истек абсолютный тайм-аут ([thread.req.timing]), указанный вabs_­time , в противном случаеcv_­status​::​no_­timeout.

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).

template <class Rep, class Period> cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time);

Requires:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком, и либо

  • никакой другой поток не ожидает этогоcondition_­variable объекта или

  • lock.mutex() возвращает то же значение для каждого изlock аргументов , поставляемых всех одновременно ожидания ( с помощьюwait,wait_­forили wait_­untilнитей).

Effects: Эквивалентен:

return wait_until(lock, chrono::steady_clock::now() + rel_time);

Returns:cv_­status​::​timeout если истек относительный тайм-аут ([thread.req.timing]), указанный вrel_­time , в противном случаеcv_­status​::​no_­timeout.

Remarks: Если функция не удовлетворяет постусловию,terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ]end note

Postconditions:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком.

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).

template <class Clock, class Duration, class Predicate> bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);

Requires:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком, и либо

  • никакой другой поток не ожидает этогоcondition_­variable объекта или

  • lock.mutex() возвращает то же значение для каждого изlock аргументов , поставляемых всех одновременно ожидания ( с помощьюwait, wait_­forилиwait_­untilнитей).

Effects: Эквивалентен:

while (!pred())
  if (wait_until(lock, abs_time) == cv_status::timeout)
    return pred();
return true;

Remarks: Если функция не удовлетворяет постусловию,terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ]end note

Postconditions:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком.

[ Note: Возвращенное значение указывает, оценивается ли предикат true независимо от того, был ли установлен тайм-аут. ]end note

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]) или любое исключение, созданноеpred.

template <class Rep, class Period, class Predicate> bool wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);

Requires:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком, и либо

  • никакой другой поток не ожидает этогоcondition_­variable объекта или

  • lock.mutex() возвращает то же значение для каждого изlock аргументов , поставляемых всех одновременно ожидания ( с помощьюwait,wait_­forили wait_­untilнитей).

Effects: Эквивалентен:

return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));

[ Note: Блокировка отсутствует, еслиpred() изначальноtrue, даже если время ожидания уже истекло. ]end note

Remarks: Если функция не удовлетворяет постусловию,terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ]end note

Postconditions:lock.owns_­lock() являетсяtrue иlock.mutex() заблокирован вызывающим потоком.

[ Note: Возвращенное значение указывает, оценивается ли предикатtrue независимо от того, был ли установлен тайм-аут. ]end note

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]) или любое исключение, созданноеpred.

33.5.4 Class condition_­variable_­any [thread.condition.condvarany]

Lock Типа должны отвечать BasicLockable требованиям. [ Note: Все стандартные типы мьютексов соответствуют этому требованию. Если используетсяLock тип, отличный от одного из стандартных типов мьютекса илиunique_­lock оболочки для стандартного типа мьютексаcondition_­variable_­any, пользователь должен убедиться, что вся необходимая синхронизация выполняется по отношению к предикату, связанному сcondition_­variable_­any экземпляром. ]end note

namespace std {
  class condition_variable_any {
  public:
    condition_variable_any();
    ~condition_variable_any();

    condition_variable_any(const condition_variable_any&) = delete;
    condition_variable_any& operator=(const condition_variable_any&) = delete;

    void notify_one() noexcept;
    void notify_all() noexcept;
    template <class Lock>
      void wait(Lock& lock);
    template <class Lock, class Predicate>
      void wait(Lock& lock, Predicate pred);

    template <class Lock, class Clock, class Duration>
      cv_status wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time);
    template <class Lock, class Clock, class Duration, class Predicate>
      bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time,
        Predicate pred);
    template <class Lock, class Rep, class Period>
      cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);
    template <class Lock, class Rep, class Period, class Predicate>
      bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time,
        Predicate pred);
  };
}

condition_variable_any();

Effects: Создает объект типаcondition_­variable_­any.

Throws:bad_­alloc илиsystem_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • resource_­unavailable_­try_­again - если какое-то ограничение ресурсов, не связанных с памятью, препятствует инициализации.

  • operation_­not_­permitted - если поток не имеет права выполнять операцию.

~condition_variable_any();

Requires: Не должно быть блокировок резьбы*this. [ Note: То есть все потоки должны быть уведомлены; впоследствии они могут заблокировать блокировку, указанную в ожидании. Это ослабляет обычные правила, которые требовали бы, чтобы все вызовы ожидания выполнялись до уничтожения. Только уведомление для разблокировки ожидания должно произойти до уничтожения. Пользователь должен позаботиться о том, что ни один из потоков не ждать*this когда был запущен деструктор, особенно когда ожидающие потоки вызова функции ожидания в цикле или с использованием перегрузокwait,wait_­forилиwait_­until что взять предикат. ]end note

Effects: Разрушает объект.

void notify_one() noexcept;

Effects: Если какие-либо потоки заблокированы в ожидании*this, разблокирует один из этих потоков.

void notify_all() noexcept;

Effects: Разблокирует все потоки, которые заблокированы в ожидании*this.

template <class Lock> void wait(Lock& lock);

[ Note: Если какая-либо изwait функций завершается через исключение, не указывается,Lock удерживается ли она. Можно использоватьLock тип, который позволяет запрашивать это, напримерunique_­lock оболочку. ]end note

Effects:

  • Атомарно звонитlock.unlock() и блокируется*this.

  • При разблокировке вызываетlock.lock() (возможно, блокировку по блокировке) и возвращает.

  • Функция разблокируется при получении сигнала от вызоваnotify_­one(), вызоваnotify_­all()или ложного сигнала .

Remarks: Если функция не удовлетворяет постусловию,terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ]end note

Postconditions:lock заблокирован вызывающим потоком.

Throws: Ничего такого.

template <class Lock, class Predicate> void wait(Lock& lock, Predicate pred);

Effects: Эквивалентен:

while (!pred())
  wait(lock);

template <class Lock, class Clock, class Duration> cv_status wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time);

Effects:

  • Атомарно звонитlock.unlock() и блокируется*this.

  • При разблокировке вызываетlock.lock() (возможно, блокировку по блокировке) и возвращает.

  • Функция разблокируется при получении сигнала от вызоваnotify_­one(), вызоваnotify_­all(), истечения абсолютного тайм-аута ([thread.req.timing]), указанного с помощьюabs_­time, или ложно.

  • Если функция завершается через исключение, онаlock.lock() должна вызываться до выхода из функции.

Remarks: Если функция не удовлетворяет постусловию,terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ]end note

Postconditions:lock заблокирован вызывающим потоком.

Returns:cv_­status​::​timeout если истек абсолютный тайм-аут ([thread.req.timing]), указанный вabs_­time , в противном случаеcv_­status​::​no_­timeout.

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).

template <class Lock, class Rep, class Period> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);

Effects: Эквивалентен:

return wait_until(lock, chrono::steady_clock::now() + rel_time);

Returns:cv_­status​::​timeout если истек относительный тайм-аут ([thread.req.timing]), указанный вrel_­time , в противном случаеcv_­status​::​no_­timeout.

Remarks: Если функция не удовлетворяет постусловию,terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ]end note

Postconditions:lock заблокирован вызывающим потоком.

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).

template <class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);

Effects: Эквивалентен:

while (!pred())
  if (wait_until(lock, abs_time) == cv_status::timeout)
    return pred();
return true;

[ Note: Блокировка отсутствует, еслиpred() она установлена ​​изначальноtrueили если время ожидания уже истекло. ]end note

[ Note: Возвращенное значение указывает, оценивается ли предикатtrue независимо от того, был ли установлен тайм-аут. ] end note

template <class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);

Effects: Эквивалентен:

return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));