33 Thread support library [thread]

33.1 General [thread.general]

В следующих подпунктах описываются компоненты для создания и управления threads, выполнения взаимного исключения и передачи условий и значений между потоками, как показано в таблице 140.

Таблица 140 - Сводка библиотеки поддержки потоков
Подпункт Заголовок (ы)
[thread.req] Требования
[thread.threads] Потоки <thread>
[thread.mutex] Взаимное исключение <mutex>
<shared_­mutex>
[thread.condition] Переменные условия <condition_­variable>
[futures] Фьючерсы <future>

33.2 Requirements [thread.req]

33.2.1 Template parameter names [thread.req.paramname]

В этом разделе имена параметров шаблона используются для выражения требований к типу. Если параметр шаблона назван Predicate, operator() примененный к аргументу шаблона должен возвращать значение, в которое можно преобразовать bool.

33.2.2 Exceptions [thread.req.exception]

Некоторые функции, описанные в этом разделе, указаны для создания исключений определенного типа system_­error. Такие исключения должны выдаваться, если обнаруживается какое-либо из условий ошибки функции или вызов операционной системы или другого базового API приводит к ошибке, которая не позволяет библиотечной функции соответствовать ее спецификациям. Об отказе в выделении памяти должно быть сообщено, как описано в [res.on.exception.handling].

[ Example: Рассмотрим функцию в этом предложении, которая определена для создания исключений определенного типа system_­error и задает условия ошибки, которые включают в себя operation_­not_­permitted для потока, который не имеет привилегии для выполнения операции. Предположим , что во время выполнения этой функции, errno из EPERM сообщается на API вызова POSIX , используемой реализации. Поскольку POSIX специфицирует errno из EPERM когда «абонент не имеет полномочий для выполнения операции», реализация карт EPERM на error_­condition из operation_­not_­permitted ([syserr]) и исключения типа system_­error выбрасывается. ]end example

Сообщение, error_­code сообщаемое code() функцией-членом такого исключения, должно сравниваться с одним из условий, указанных в элементе условия ошибки функции.

33.2.3 Native handles [thread.req.native]

Некоторые классы, описанные в этом разделе, имеют члены native_­handle_­type и native_­handle. Наличие этих членов и их семантика определяется реализацией. [ Note: Эти члены позволяют реализациям предоставлять доступ к деталям реализации. Их имена указаны для облегчения обнаружения переносимого времени компиляции. Фактическое использование этих членов по своей природе непереносимо. ] end note

33.2.4 Timing specifications [thread.req.timing]

Некоторые функции, описанные в этом разделе, принимают аргумент для указания тайм-аута. Эти тайм-ауты указываются либо как, либо duration как time_­point тип, как указано в [time].

Реализации обязательно имеют некоторую задержку при возврате из тайм-аута. Любые накладные расходы в ответе на прерывание, возврате функции и планировании вызывают задержку «качества реализации», выражаемую как продолжительность Di. В идеале эта задержка должна быть равна нулю. Кроме того, любая конкуренция за ресурсы процессора и памяти вызывает задержку «качества управления», выраженную в продолжительности Dm. Продолжительность задержки может варьироваться от тайм-аута к тайм-ауту, но во всех случаях чем короче, тем лучше.

Функции-члены, имена которых заканчиваются на, _­for принимают аргумент, определяющий продолжительность. Эти функции производят относительные таймауты. Реализации должны использовать устойчивые часы для измерения времени для этих функций.330 Учитывая аргумент продолжительности Dt, продолжительность тайм-аута в реальном времени равна Dt+Di+Dm.

Функции-члены, имена которых заканчиваются на, _­until принимают аргумент, указывающий момент времени. Эти функции производят абсолютные таймауты. Реализации должны использовать часы, указанные в момент времени, для измерения времени для этих функций. Учитывая аргумент точки времени часов, точка Ctвремени часов для возврата из тайм-аута должна быть, Ct+Di+Dm когда часы не корректируются во время тайм-аута. Если часы настроены на время Ca во время тайм-аута, поведение должно быть следующим:

  • if Ca>Ct, функция ожидания должна проснуться как можно скорее, т. е. Ca+Di+Dmпоскольку тайм-аут уже выполнен. [ Note: Эта спецификация может привести к уменьшению общей продолжительности ожидания при измерении по установившимся часам. ] end note

  • if Ca<=Ct, функция ожидания не должна истекать по тайм-ауту до тех пор, пока не Clock​::​now() вернет время Cn>=Ct, т. е. пробуждение в Ct+Di+Dm. [ Note: Когда часы настраиваются в обратном направлении, эта спецификация может привести к увеличению общей продолжительности ожидания при измерении по сравнению с установившимися часами. Когда часы настраиваются вперед, эта спецификация может привести к уменьшению общей продолжительности ожидания при измерении по сравнению с устойчивыми часами. ] end note

Реализация должна вернуться из такого тайм-аута в любой момент от времени, указанного выше, до времени, когда она вернется из установившегося относительного тайм-аута по разнице между Ct и моментом времени вызова _­until функции. [ Note: Реализации должны уменьшать продолжительность ожидания, когда часы переводятся вперед. ]end note

[ Note: Если часы не синхронизированы с постоянными часами, например часами процессора, эти тайм-ауты могут не обеспечивать полезные функции. ] end note

Разрешение синхронизации, обеспечиваемое реализацией, зависит как от операционной системы, так и от оборудования. Наилучшее разрешение, обеспечиваемое реализацией, называется native resolution.

Обеспечиваемые реализацией часы, которые используются для этих функций, должны соответствовать TrivialClock требованиям.

Функция, которая принимает аргумент, определяющий тайм-аут, будет сгенерирована, если во время ее выполнения часы, момент времени или продолжительность выдают исключение. Такие исключения обозначаются как timeout-related exceptions. [ Note: Экземпляры типов часов, времени и продолжительности, предоставленные реализацией, как указано в [time.clock] , не вызывают исключений. ]end note

Все реализации, для которых значимы стандартные единицы времени, обязательно должны иметь устойчивые часы в своей аппаратной реализации.

33.2.5 Requirements for Lockable types [thread.req.lockable]

33.2.5.1 In general [thread.req.lockable.general]

An execution agent - это объект, такой как поток, который может выполнять работу параллельно с другими агентами выполнения. [ Note: Реализации или пользователи могут вводить другие виды агентов, такие как процессы или задачи пула потоков. ] Вызывающий агент определяется контекстом, например вызывающим потоком, который содержит вызов, и так далее. end note

[ Note: Некоторые блокируемые объекты «не замечают агента» в том смысле, что они работают для любой модели исполняющего агента, потому что они не определяют и не хранят идентификатор агента (например, обычная блокировка спина). ] end note

Стандартные шаблоны библиотеки unique_­lock ([thread.lock.unique]), shared_­lock ([thread.lock.shared]), scoped_­lock ([thread.lock.scoped]), lock_­guard ([thread.lock.guard]) lock,, try_­lock ([thread.lock.algorithm]) и condition_­variable_­any ([thread.condition.condvarany]) работают с блокируемыми объектами, предоставляемыми пользователем. В BasicLockable требованиях, Lockable требованиях и TimedLockable требованиях перечислены требования, налагаемые этими типами библиотек, чтобы получить или освободить право собственности на объект lock для данного агента выполнения. [ Note: Характер владения блокировкой и любая синхронизация, которую она может повлечь за собой, не являются частью этих требований. ] end note

33.2.5.2 BasicLockable requirements [thread.req.lockable.basic]

Тип L соответствует BasicLockable требованиям, если следующие выражения правильно сформированы и имеют указанную семантику (m обозначает значение типа L).

m.lock()

Effects: Блокируется до тех пор, пока для текущего агента выполнения не будет получена блокировка. Если выбрасывается исключение, то для текущего агента выполнения блокировка не должна быть получена.

m.unlock()

Requires: Текущий агент исполнения должен удерживать блокировку m.

Effects: Снимает блокировку, m удерживаемую текущим агентом выполнения.

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

33.2.5.3 Lockable requirements [thread.req.lockable.req]

Тип L соответствует Lockable требованиям, если он соответствует BasicLockable требованиям, а следующие выражения правильно сформированы и имеют указанную семантику (m обозначает значение типа L).

m.try_lock()

Effects: Пытается получить блокировку для текущего агента выполнения без блокировки. Если выбрасывается исключение, то для текущего агента выполнения блокировка не должна быть получена.

Return type: bool.

Returns: true если блокировка была приобретена, false иначе.

33.2.5.4 TimedLockable requirements [thread.req.lockable.timed]

Тип L соответствует TimedLockable требованиям, если он соответствует Lockable требованиям, а следующие выражения правильно сформированы и имеют указанную семантику (m обозначает значение типа L, rel_­time обозначает значение экземпляра durationи abs_­time обозначает значение экземпляра time_­point).

m.try_lock_for(rel_time)

Effects: Пытается получить блокировку для текущего агента выполнения в течение относительного таймаута ([thread.req.timing]), указанного в rel_­time. Функция не должна возвращаться в течение тайм-аута, указанного в, rel_­time если она не получила блокировку m для текущего агента выполнения. Если выбрасывается исключение, то для текущего агента выполнения блокировка не должна быть получена.

Return type: bool.

Returns: true если блокировка была приобретена, false иначе.

m.try_lock_until(abs_time)

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

Return type: bool.

Returns: true если блокировка была приобретена, false иначе.

33.2.6 decay_­copy [thread.decaycopy]

В нескольких местах этого пункта используется операция . Все такие использования означают вызов функции и использование результата, который определяется следующим образом:DECAY_­COPY(x) decay_­copy(x) decay_­copy

template <class T> decay_t<T> decay_copy(T&& v)
  { return std::forward<T>(v); }

33.3 Threads [thread.threads]

[thread.threads] описывает компоненты, которые можно использовать для создания потоков и управления ими. [ Note: Эти потоки предназначены для взаимно-однозначного сопоставления с потоками операционной системы. ]end note

33.3.1 Header <thread> synopsis [thread.syn]

namespace std {
  class thread;

  void swap(thread& x, thread& y) noexcept;

  namespace this_thread {
    thread::id get_id() noexcept;

    void yield() noexcept;
    template <class Clock, class Duration>
      void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
    template <class Rep, class Period>
      void sleep_for(const chrono::duration<Rep, Period>& rel_time);
  }
}

33.3.2 Class thread [thread.thread.class]

Класс thread предоставляет механизм для создания нового потока выполнения, для соединения с потоком (т. Е. Ожидания завершения потока) и для выполнения других операций, которые управляют и запрашивают состояние потока. thread Объект однозначно представляет собой конкретный поток выполнения. Это представление может быть передано другим thread объектам таким образом, чтобы никакие два thread объекта одновременно не представляли один и тот же поток выполнения. Поток исполнения - это detached когда никакой thread объект не представляет этот поток. Объекты класса thread могут находиться в состоянии, не представляющем поток выполнения. [ Объект не представляет собой поток выполнения после окончания строительства по умолчанию, после перемещения из, или после успешного вызова или . ]Note: thread detach join end note

namespace std {
  class thread {
  public:
    // types:
    class id;
    using native_handle_type = implementation-defined; // See [thread.req.native]

    // construct/copy/destroy:
    thread() noexcept;
    template <class F, class... Args> explicit thread(F&& f, Args&&... args);
    ~thread();
    thread(const thread&) = delete;
    thread(thread&&) noexcept;
    thread& operator=(const thread&) = delete;
    thread& operator=(thread&&) noexcept;

    // members:
    void swap(thread&) noexcept;
    bool joinable() const noexcept;
    void join();
    void detach();
    id get_id() const noexcept;
    native_handle_type native_handle(); // See [thread.req.native]

    // static members:
    static unsigned hardware_concurrency() noexcept;
  };
}

33.3.2.1 Class thread​::​id [thread.thread.id]

namespace std {
  class thread::id {
  public:
      id() noexcept;
  };

  bool operator==(thread::id x, thread::id y) noexcept;
  bool operator!=(thread::id x, thread::id y) noexcept;
  bool operator<(thread::id x, thread::id y) noexcept;
  bool operator<=(thread::id x, thread::id y) noexcept;
  bool operator>(thread::id x, thread::id y) noexcept;
  bool operator>=(thread::id x, thread::id y) noexcept;

  template<class charT, class traits>
    basic_ostream<charT, traits>&
      operator<< (basic_ostream<charT, traits>& out, thread::id id);

  // Hash support
  template <class T> struct hash;
  template <> struct hash<thread::id>;
}

Объект типа thread​::​id предоставляет уникальный идентификатор для каждого потока выполнения и одно отличное значение для всех thread объектов, которые не представляют поток выполнения ([thread.thread.class]). Каждый поток выполнения имеет связанный thread​::​id объект, который не равен thread​::​id объекту любого другого потока выполнения и который не равен thread​::​id объекту любого thread объекта, который не представляет потоки выполнения.

thread​::​id должен быть trivially copyable class. Библиотека может повторно использовать значение thread​::​id завершенного потока, который больше не может быть присоединен.

[ Note: Операторы отношения позволяют использовать thread​::​id объекты в качестве ключей в ассоциативных контейнерах. ] end note

id() noexcept;

Effects: Создает объект типа id.

Postconditions: Созданный объект не представляет собой поток выполнения.

bool operator==(thread::id x, thread::id y) noexcept;

Returns: true только в том случае, если x и y представляют один и тот же поток выполнения или не представляют x ни y один поток выполнения.

bool operator!=(thread::id x, thread::id y) noexcept;

Returns: !(x == y)

bool operator<(thread::id x, thread::id y) noexcept;

Returns: Такое значение, которое operator< представляет собой общий порядок, как описано в [alg.sorting].

bool operator<=(thread::id x, thread::id y) noexcept;

Returns: !(y < x).

bool operator>(thread::id x, thread::id y) noexcept;

Returns: y < x.

bool operator>=(thread::id x, thread::id y) noexcept;

Returns: !(x < y).

template<class charT, class traits> basic_ostream<charT, traits>& operator<< (basic_ostream<charT, traits>& out, thread::id id);

Effects: Вставляет неопределенное текстовое представление id в out. Для двух объектов типа thread​::​id x и y, если x == y эти thread​::​id объекты будут иметь то же текстовое представление , и если x != y эти thread​::​id объекты имеют различные текстовые представления.

Returns: out.

template <> struct hash<thread::id>;

Специализация включена ([unord.hash]).

33.3.2.2 thread constructors [thread.thread.constr]

thread() noexcept;

Effects: Создает thread объект, не представляющий поток выполнения.

Postconditions: get_­id() == id().

template <class F, class... Args> explicit thread(F&& f, Args&&... args);

Requires:  F и каждый Ti в Args должны удовлетворять MoveConstructible требованиям. INVOKE(​DECAY_­COPY(​std​::​forward<F>(f)), DECAY_­COPY(​std​::​forward<Args>(​args))...) ([func.require]) должно быть допустимым выражением.

Remarks: Этот конструктор не должен участвовать в разрешении перегрузки, если decay_­t<F> он того же типа, что и std​::​thread.

Effects:  Создает объект типа thread. Новый поток выполнения выполняется INVOKE(​DECAY_­COPY(​std​::​forward<F>(f)), DECAY_­COPY(​std​::​forward<Args>(​args))...) с вызовами, DECAY_­COPY которые оцениваются в потоке построения. Любое возвращаемое значение из этого вызова игнорируется. [ Note: Это означает, что любые исключения, не возникшие при вызове копии, f будут выброшены в потоке построения, а не в новом потоке. ] Если вызов завершается неперехваченным исключением, должен вызываться. end noteINVOKE(​DECAY_­COPY(​std​::​forward<F>(f)), DECAY_­COPY(​std​::​forward<Args>(args))...) terminate

Synchronization: Завершение вызова конструктора синхронизируется с началом вызова копии f.

Postconditions: get_­id() != id(). *this представляет только что запущенный поток.

Throws: system_­error если не удается начать новый поток.

Error conditions:

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

thread(thread&& x) noexcept;

Effects: Создает объект типа thread из xи устанавливает x в сконструированное состояние по умолчанию.

Postconditions: x.get_­id() == id() и get_­id() возвращает значение x.get_­id() до начала строительства.

33.3.2.3 thread destructor [thread.thread.destr]

~thread();

Если joinable(), звонит terminate(). В противном случае не имеет никакого эффекта. [ Note: Неявное отсоединение или присоединение к joinable() потоку в его деструкторе может привести к трудностям в отладке ошибок корректности (для отсоединения) или производительности (для присоединения), обнаруживаемых только при возникновении исключения. Таким образом, программист должен гарантировать, что деструктор никогда не будет выполняться, пока поток все еще может быть присоединен. ] end note

33.3.2.4 thread assignment [thread.thread.assign]

thread& operator=(thread&& x) noexcept;

Effects: Если joinable(), звонит terminate(). В противном случае, присваивает состояние , x чтобы *this и множеств x в состояние по умолчанию строится.

Postconditions: x.get_­id() == id() и get_­id() возвращает значение x.get_­id() до присвоения.

Returns: *this.

33.3.2.5 thread members [thread.thread.member]

void swap(thread& x) noexcept;

Effects: Меняет местами состояние *this и x.

bool joinable() const noexcept;

Returns: get_­id() != id().

void join();

Effects:  Блокируется до тех пор, пока поток, представленный значком, *this не будет завершен.

Synchronization: Завершение потока, представленное *this synchronizes with соответствующим успешным join() возвратом. [ Note: Операции на *this не синхронизированы. ] end note

Postconditions: Цепочка, представленная *this значком, завершена. get_­id() == id().

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

Error conditions:

  • resource_­deadlock_­would_­occur - при обнаружении тупика или get_­id() == this_­thread​::​​get_­id().

  • no_­such_­process - если нить недействительна.

  • invalid_­argument - если поток не может быть присоединен.

void detach();

Effects: Поток, представленный как, *this продолжает выполнение без блокировки вызывающего потока. Когда detach() возвращается, *this больше не представляет собой возможно продолжающийся поток выполнения. Когда поток, ранее представленный как *this завершает выполнение, реализация должна освободить все принадлежащие ей ресурсы.

Postconditions: get_­id() == id().

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

Error conditions:

  • no_­such_­process - если нить недействительна.

  • invalid_­argument - если поток не может быть присоединен.

id get_id() const noexcept;

Returns: Созданный по умолчанию id объект, если *this он не представляет поток, в противном случае this_­thread​::​get_­id() для потока выполнения, представленного *this.

33.3.2.6 thread static members [thread.thread.static]

unsigned hardware_concurrency() noexcept;

Returns: Количество контекстов аппаратного потока. [ Note: Это значение следует рассматривать только как подсказку. ] Если это значение не вычислимо или точно не определено, реализация должна вернуть 0. end note

33.3.2.7 thread specialized algorithms [thread.thread.algorithm]

void swap(thread& x, thread& y) noexcept;

Effects: Как будто мимо x.swap(y).

33.3.3 Namespace this_­thread [thread.thread.this]

namespace std::this_thread {
  thread::id get_id() noexcept;

  void yield() noexcept;
  template <class Clock, class Duration>
    void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
  template <class Rep, class Period>
    void sleep_for(const chrono::duration<Rep, Period>& rel_time);
}

thread::id this_thread::get_id() noexcept;

Returns: Объект типа, thread​::​id который однозначно идентифицирует текущий поток выполнения. Никакой другой поток выполнения не должен иметь этого идентификатора, и этот поток выполнения всегда должен иметь этот идентификатор. Возвращенный объект не должен сравниваться с созданным по умолчанию thread​::​id.

void this_thread::yield() noexcept;

Effects: Предлагает реализации возможность переноса графика.

Synchronization: Никто.

template <class Clock, class Duration> void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);

Effects: Блокирует вызывающий поток на время абсолютного тайм-аута ([thread.req.timing]), указанное в abs_­time.

Synchronization: Никто.

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

template <class Rep, class Period> void sleep_for(const chrono::duration<Rep, Period>& rel_time);

Effects: Блокирует вызывающий поток в течение относительного времени ожидания ([thread.req.timing]), указанного в rel_­time.

Synchronization: Никто.

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

33.4 Mutual exclusion [thread.mutex]

В этом разделе представлены механизмы взаимного исключения: мьютексы, блокировки и однократный вызов. Эти механизмы упрощают создание программ, свободных от расы ([intro.multithread]).

33.4.1 Header <mutex> synopsis [mutex.syn]

namespace std {
  class mutex;
  class recursive_mutex;
  class timed_mutex;
  class recursive_timed_mutex;

  struct defer_lock_t { explicit defer_lock_t() = default; };
  struct try_to_lock_t { explicit try_to_lock_t() = default; };
  struct adopt_lock_t { explicit adopt_lock_t() = default; };

  inline constexpr defer_lock_t  defer_lock { };
  inline constexpr try_to_lock_t try_to_lock { };
  inline constexpr adopt_lock_t  adopt_lock { };

  template <class Mutex> class lock_guard;
  template <class... MutexTypes> class scoped_lock;
  template <class Mutex> class unique_lock;

  template <class Mutex>
    void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;

  template <class L1, class L2, class... L3> int try_lock(L1&, L2&, L3&...);
  template <class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);

  struct once_flag;

  template<class Callable, class... Args>
    void call_once(once_flag& flag, Callable&& func, Args&&... args);
}

33.4.2 Header <shared_­mutex> synopsis [shared_mutex.syn]

namespace std {
  class shared_mutex;
  class shared_timed_mutex;
  template <class Mutex> class shared_lock;
  template <class Mutex>
    void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
}

33.4.3 Mutex requirements [thread.mutex.requirements]

33.4.3.1 In general [thread.mutex.requirements.general]

Объект мьютекса обеспечивает защиту от гонок данных и обеспечивает безопасную синхронизацию данных между ними execution agents. Агент выполнения owns - мьютекс с момента успешного вызова одной из функций блокировки до вызова разблокировки. Мьютексы могут быть рекурсивными или нерекурсивными и могут предоставлять одновременное владение одному или нескольким агентам выполнения. Поставляются как рекурсивные, так и нерекурсивные мьютексы.

33.4.3.2 Mutex types [thread.mutex.requirements.mutex]

mutex types Стандартные библиотеки типов mutex, recursive_­mutex, timed_­mutex, recursive_­timed_­mutex, shared_­mutex, и shared_­timed_­mutex. Они должны соответствовать требованиям, изложенным в этом разделе. В этом описании m обозначает объект типа мьютекса.

Типы мьютексов должны соответствовать Lockable требованиям.

Типы мьютексов должны быть DefaultConstructible и Destructible. Если инициализация объекта типа мьютекса не удалась,system_­error должно быть выброшено исключение типа . Типы мьютексов нельзя копировать или перемещать.

Условия ошибки для кодов ошибок, если таковые имеются, сообщаемые функциями-членами типов мьютексов, должны быть:

  • resource_­unavailable_­try_­again - если какой-либо управляемый тип дескриптора недоступен.

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

  • invalid_­argument - если какой-либо собственный тип дескриптора, манипулируемый как часть конструкции мьютекса, неверен.

Реализация должна обеспечивать операции блокировки и разблокировки, как описано ниже. В целях определения наличия гонки данных они ведут себя как атомарные операции ([intro.multithread]). Операции блокировки и разблокировки на одном мьютексе должны выполняться в едином общем порядке. [ Note: Это можно рассматривать как modification order мьютекс. ] [ Создание и уничтожение объекта типа мьютекса не обязательно должно быть потокобезопасным; следует использовать другую синхронизацию, чтобы гарантировать, что объекты мьютекса инициализированы и видны другим потокам. ] end noteNote: end note

Выражение m.lock() должно быть правильно сформированным и иметь следующую семантику:

Requires: Если m имеет тип mutex, timed_­mutex, shared_­mutexили shared_­timed_­mutex, вызывающий поток не является владельцем мьютекса.

Effects: Блокирует вызывающий поток до тех пор, пока для вызывающего потока не будет получено право владения мьютексом.

Postconditions: Вызывающий поток владеет мьютексом.

Return type: void.

Synchronization: Предыдущие unlock() операции на одном и том же объекте являются synchronize with этой операцией.

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

Error conditions:

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

  • resource_­deadlock_­would_­occur - если реализация обнаруживает, что возникнет тупик.

Выражение m.try_­lock() должно быть правильно сформированным и иметь следующую семантику:

Requires: Если m имеет тип mutex, timed_­mutex, shared_­mutexили shared_­timed_­mutex, вызывающий поток не является владельцем мьютекса.

Effects: Пытается получить право собственности на мьютекс для вызывающего потока без блокировки. Если право собственности не получено, нет никакого эффекта и try_­lock() немедленно возвращается. Реализация может не получить блокировку, даже если она не удерживается каким-либо другим потоком. [ Note: Этот ложный сбой обычно встречается редко, но допускает интересные реализации, основанные на простом сравнении и обмене (пункт [atomics]). ] Реализация должна гарантировать, что не будет последовательного возврата в отсутствие конкурирующих захватов мьютексов. end note try_­lock() false

Return type: bool.

Returns: true если право собственности на мьютекс было получено для вызывающего потока, в противном случае false.

Synchronization: Если try_­lock() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция. [ Note: Так lock() как не синхронизируется с неудачным последующим try_­lock(), правила видимости достаточно слабы, чтобы о состоянии после отказа было бы мало что известно, даже при отсутствии ложных отказов. ] end note

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

Выражение m.unlock() должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток должен владеть мьютексом.

Effects: Освобождает вызывающий поток от владения мьютексом.

Return type: void.

Synchronization: Эта операция является synchronizes with последующими операциями блокировки, которые получают право собственности на один и тот же объект.

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

33.4.3.2.1 Class mutex [thread.mutex.class]

namespace std {
  class mutex {
  public:
    constexpr mutex() noexcept;
    ~mutex();

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

    void lock();
    bool try_lock();
    void unlock();

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

Класс mutex предоставляет нерекурсивный мьютекс с семантикой исключительного владения. Если один поток владеет объектом мьютекса, попытки другого потока получить право владения этим объектом будут неудачными (для try_­lock()) или блокируются (для lock()) до тех пор, пока поток-владелец не освободит владение с помощью вызова unlock().

[ Note: После того, как поток A вызвал unlock(), освободив мьютекс, другой поток B может заблокировать тот же мьютекс, заметить, что он больше не используется, разблокировать его и уничтожить до того, как поток, A кажется, вернется из своего вызова разблокировки. Для правильной обработки таких сценариев требуются реализации, если поток A не обращается к мьютексу после возврата вызова разблокировки. Эти случаи обычно возникают, когда объект со счетчиком ссылок содержит мьютекс, который используется для защиты счетчика ссылок. ]end note

Класс mutex должен удовлетворять всем требованиям mutex requirements. Это должен быть standard-layout class.

[ Note: Программа может заблокироваться, если поток, которому принадлежит mutex объект, вызывает lock() этот объект. Если реализация может обнаружить тупик, resource_­deadlock_­would_­occur может наблюдаться состояние ошибки. ] end note

Поведение программы не определено, если она уничтожает mutex объект, принадлежащий любому потоку, или поток завершается, пока владеет mutex объектом.

33.4.3.2.2 Class recursive_­mutex [thread.mutex.recursive]

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

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

    void lock();
    bool try_lock() noexcept;
    void unlock();

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

Класс recursive_­mutex предоставляет рекурсивный мьютекс с семантикой исключительного владения. Если один поток владеет recursive_­mutex объектом, попытки другого потока получить право владения этим объектом завершатся ошибкой (для try_­lock()) или заблокируются (для lock()) до тех пор, пока первый поток полностью не освободит владение.

Класс recursive_­mutex должен удовлетворять всем требованиям mutex requirements. Это должен быть standard-layout class.

Поток, которому принадлежит recursive_­mutex объект, может получить дополнительные уровни владения путем вызова этого объекта lock() или try_­lock() для этого объекта. Не указано, сколько уровней владения может быть получено одним потоком. Если поток уже получил максимальный уровень владения recursive_­mutex объектом, дополнительные вызовы try_­lock() должны завершиться ошибкой, а дополнительные вызовы lock() вызовут исключение типа system_­error. Поток должен вызывать unlock() один раз для каждого уровня владения, полученного вызовами lock() и try_­lock(). Только когда все уровни владения высвобождены, право владения может быть приобретено другим потоком.

Поведение программы не определено, если:

  • он уничтожает recursive_­mutex объект, принадлежащий любому потоку, или

  • поток завершается при владении recursive_­mutex объектом.

33.4.3.3 Timed mutex types [thread.timedmutex.requirements]

timed mutex types Стандартные библиотеки типов timed_­mutex, recursive_­timed_­mutexи shared_­timed_­mutex. Они должны соответствовать требованиям, изложенным ниже. В этом описании m обозначает объект типа мьютекса, rel_­time обозначает объект создания экземпляра durationи abs_­time обозначает объект создания экземпляра time_­point.

Типы синхронизированных мьютексов должны соответствовать TimedLockable требованиям.

Выражение m.try_­lock_­for(rel_­time) должно быть правильно сформированным и иметь следующую семантику:

Requires: Если m имеет тип timed_­mutex или shared_­timed_­mutex, вызывающий поток не владеет мьютексом.

Effects: Функция пытается получить право собственности на мьютекс в течение относительного времени ожидания ([thread.req.timing]), указанного в rel_­time. Если время, указанное в, rel_­time меньше или равно rel_­time.zero(), функция пытается получить право владения без блокировки (как если бы путем вызова try_­lock()). Функция должна вернуться в течение тайм-аута, указанного в, rel_­time только если она получила право собственности на объект мьютекса. [ Note: Как и в случае try_­lock(), нет никакой гарантии, что право собственности будет получено, если блокировка доступна, но ожидается, что реализации приложат для этого серьезные усилия. ]end note

Return type: bool.

Returns: true если право собственности было получено, иначе false.

Synchronization: Если try_­lock_­for() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция.

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

Выражение m.try_­lock_­until(abs_­time) должно быть правильно сформированным и иметь следующую семантику:

Requires: Если m имеет тип timed_­mutex или shared_­timed_­mutex, вызывающий поток не владеет мьютексом.

Effects: Функция пытается получить право собственности на мьютекс. Если abs_­time он уже прошел, функция пытается получить право собственности без блокировки (как если бы путем вызова try_­lock()). Функция должна возвращаться до абсолютного тайм-аута ([thread.req.timing]), указанного в, abs_­time только если она получила право собственности на объект мьютекса. [ Note: Как и в случае try_­lock(), нет никакой гарантии, что право собственности будет получено, если блокировка доступна, но ожидается, что реализации приложат для этого серьезные усилия. ] end note

Return type: bool.

Returns: true если право собственности было получено, иначе false.

Synchronization: Если try_­lock_­until() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция.

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

33.4.3.3.1 Class timed_­mutex [thread.timedmutex.class]

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

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

    void lock();  // blocking
    bool try_lock();
    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

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

Класс timed_­mutex предоставляет нерекурсивный мьютекс с семантикой исключительного владения. Если один поток владеет timed_­mutex объектом, попытки другого потока приобрести в собственность этого объекта не получится (для try_­lock()) или блока (для lock(), try_­lock_­for()и try_­lock_­until()) до тех пор , владеющего нить не выпустило собственность с помощью вызова unlock() или вызова try_­lock_­for() или try_­lock_­until() тайма - аута (имеющего не удалось получить право собственности).

Класс timed_­mutex должен удовлетворять всем требованиям timed mutex requirements. Это должен быть standard-layout class.

Поведение программы не определено, если:

  • он уничтожает timed_­mutex объект, принадлежащий любому потоку,

  • поток , который владеет timed_­mutex объект звонки lock(), try_­lock(), try_­lock_­for()или try_­lock_­until() на этом объекте, или

  • поток завершается при владении timed_­mutex объектом.

33.4.3.3.2 Class recursive_­timed_­mutex [thread.timedmutex.recursive]

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

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

    void lock();  // blocking
    bool try_lock() noexcept;
    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

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

Класс recursive_­timed_­mutex предоставляет рекурсивный мьютекс с семантикой исключительного владения. Если один поток владеет recursive_­timed_­mutex объектом, попытки другого потока приобрести в собственность этого объекта не получится (для try_­lock()) или блок (для lock(), try_­lock_­for()и try_­lock_­until()) до тех пор , владеющее поток не полностью освобождается право собственности или вызов try_­lock_­for() или try_­lock_­until() тайм - аут (не добившись право собственности).

Класс recursive_­timed_­mutex должен удовлетворять всем требованиям timed mutex requirements. Это должен быть класс стандартной компоновки (пункт [class]).

Поток , который владеет recursive_­timed_­mutex объектом может приобрести дополнительные уровни владения вызывающего lock(), try_­lock(), try_­lock_­for()или try_­lock_­until() на этом объект. Не указано, сколько уровней владения может быть получено одним потоком. Если поток уже приобрел максимальный уровень владения для recursive_­timed_­mutex объекта, дополнительные вызовов try_­lock(), try_­lock_­for()или try_­lock_­until() ослабеют, а также дополнительные призывов lock() бросит исключение типа system_­error. Поток наречет unlock() один раз для каждого уровня владения приобретаемого вызовов lock(), try_­lock(), try_­lock_­for(), и try_­lock_­until(). Только когда все уровни владения были освобождены, владение объектом может быть приобретено другим потоком.

Поведение программы не определено, если:

  • он уничтожает recursive_­timed_­mutex объект, принадлежащий любому потоку, или

  • поток завершается при владении recursive_­timed_­mutex объектом.

33.4.3.4 Shared mutex types [thread.sharedmutex.requirements]

Стандартные библиотеки типов shared_­mutex и shared_­timed_­mutex есть shared mutex types. Типы общих мьютексов должны соответствовать requirements of mutex typesи дополнительно должны соответствовать требованиям, изложенным ниже. В этом описании m обозначает объект общего типа мьютекса.

В дополнение к режиму монопольного владения блокировкой, указанному в [thread.mutex.requirements.mutex], общие типы мьютексов предоставляют shared lock режим владения. Несколько агентов выполнения могут одновременно владеть общей блокировкой общего типа мьютекса. Но ни один агент выполнения не должен удерживать разделяемую блокировку, в то время как другой агент выполнения удерживает монопольную блокировку того же общего типа мьютекса, и наоборот. Максимальное количество исполнительных агентов, которые могут совместно использовать общую блокировку для одного общего типа мьютекса, не указано, но должно быть не менее 10000. Если больше, чем максимальное количество исполнительных агентов пытается получить общую блокировку, избыточные исполнительные агенты должны блокировать до тех пор, пока количество разделяемых блокировок не уменьшится ниже максимального значения другими исполнительными агентами, снимающими свою общую блокировку.

Выражение m.lock_­shared() должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток не владеет мьютексом.

Effects: Блокирует вызывающий поток до тех пор, пока для вызывающего потока не будет получено совместное владение мьютексом. Если выбрасывается исключение, то для текущего потока не должна быть получена разделяемая блокировка.

Postconditions: Вызывающий поток имеет общую блокировку мьютекса.

Return type: void.

Synchronization: Предыдущие unlock() операции на одном и том же объекте являются synchronize with этой операцией.

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

Error conditions:

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

  • resource_­deadlock_­would_­occur - если реализация обнаруживает, что возникнет тупик.

Выражение m.unlock_­shared() должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток должен удерживать общую блокировку мьютекса.

Effects: Снимает общую блокировку мьютекса, удерживаемого вызывающим потоком.

Return type: void.

Synchronization: Эта операция является synchronizes with последующими lock() операциями, которые получают право собственности на один и тот же объект.

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

Выражение m.try_­lock_­shared() должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток не владеет мьютексом.

Effects: Пытается получить совместное владение мьютексом для вызывающего потока без блокировки. Если долевое владение не получено, нет эффекта и try_­lock_­shared() немедленно возвращается. Реализация может не получить блокировку, даже если она не удерживается каким-либо другим потоком.

Return type: bool.

Returns: true если была приобретена блокировка долевого владения, в false противном случае.

Synchronization: Если try_­lock_­shared() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция.

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

33.4.3.4.1 Class shared_mutex [thread.sharedmutex.class]

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

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

    // Exclusive ownership
    void lock(); // blocking
    bool try_lock();
    void unlock();

    // Shared ownership
    void lock_shared(); // blocking
    bool try_lock_shared();
    void unlock_shared();

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

Класс shared_­mutex предоставляет нерекурсивный мьютекс с семантикой совместного владения.

Класс shared_­mutex должен удовлетворять всем требованиям shared mutex requirements. Это должен быть класс стандартной компоновки (пункт [class]).

Поведение программы не определено, если:

  • он уничтожает shared_­mutex объект, принадлежащий любому потоку,

  • поток пытается рекурсивно получить право собственности на a shared_­mutex, или

  • поток завершается, когда ему принадлежит какое-либо право собственности на a shared_­mutex.

shared_­mutex может быть синонимом shared_­timed_­mutex.

33.4.3.5 Shared timed mutex types [thread.sharedtimedmutex.requirements]

Стандартный тип библиотеки shared_­timed_­mutex - это shared timed mutex type. Общие синхронизированных типы взаимных блокировок должны отвечать требованиям timed mutex types, shared mutex typesи , кроме того , должны отвечать требованиям , изложенным ниже. В этом описании m обозначает объект общего типа синхронизированного мьютекса, rel_­type обозначает объект создания экземпляра durationи abs_­time обозначает объект создания экземпляра time_­point.

Выражение m.try_­lock_­shared_­for(rel_­time) должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток не владеет мьютексом.

Effects: Пытается получить право владения разделяемой блокировкой для вызывающего потока в течение относительного времени ожидания ([thread.req.timing]), указанного в rel_­time. Если время, указанное в, rel_­time меньше или равно rel_­time.zero(), функция пытается получить право владения без блокировки (как если бы путем вызова try_­lock_­shared()). Функция должна вернуться в течение тайм-аута, указанного в, rel_­time только если она получила совместное владение объектом мьютекса. [ Note: Как и в случае try_­lock(), нет никакой гарантии, что право собственности будет получено, если блокировка доступна, но ожидается, что реализации приложат для этого серьезные усилия. ] Если выбрасывается исключение, то для текущего потока не должна быть получена разделяемая блокировка. end note

Return type: bool.

Returns: true если была получена разделяемая блокировка, false иначе.

Synchronization: Если try_­lock_­shared_­for() возвращается true, предыдущие unlock() операции с тем же объектом синхронизируются с ([intro.multithread]) этой операцией.

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

Выражение m.try_­lock_­shared_­until(abs_­time) должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток не владеет мьютексом.

Effects: Функция пытается получить совместное владение мьютексом. Если abs_­time он уже прошел, функция пытается получить совместное владение без блокировки (как если бы путем вызова try_­lock_­shared()). Функция должна возвращаться до истечения абсолютного тайм-аута ([thread.req.timing]), указанного в, abs_­time только если она получила совместное владение объектом мьютекса. [ Note: Как и в случае try_­lock(), нет никакой гарантии, что право собственности будет получено, если блокировка доступна, но ожидается, что реализации приложат для этого серьезные усилия. ] Если выбрасывается исключение, то для текущего потока не должна быть получена разделяемая блокировка. end note

Return type: bool.

Returns: true если была получена разделяемая блокировка, false иначе.

Synchronization: Если try_­lock_­shared_­until() возвращается true, предыдущие unlock() операции с тем же объектом синхронизируются с ([intro.multithread]) этой операцией.

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

33.4.3.5.1 Class shared_­timed_­mutex [thread.sharedtimedmutex.class]

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

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

    // Exclusive ownership
    void lock();  // blocking
    bool try_lock();
    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    // Shared ownership
    void lock_shared();  // blocking
    bool try_lock_shared();
    template <class Rep, class Period>
      bool
      try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool
      try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock_shared();
  };
}

Класс shared_­timed_­mutex предоставляет нерекурсивный мьютекс с семантикой совместного владения.

Класс shared_­timed_­mutex должен удовлетворять всем требованиям shared timed mutex requirements. Это должен быть класс стандартной компоновки (пункт [class]).

Поведение программы не определено, если:

  • он уничтожает shared_­timed_­mutex объект, принадлежащий любому потоку,

  • поток пытается рекурсивно получить право собственности на a shared_­timed_­mutex, или

  • поток завершается, когда ему принадлежит какое-либо право собственности на a shared_­timed_­mutex.

33.4.4 Locks [thread.lock]

A lock - это объект, который содержит ссылку на блокируемый объект и может разблокировать блокируемый объект во время разрушения блокировки (например, при выходе из области действия блока). Агент выполнения может использовать блокировку, чтобы помочь в управлении владением блокируемым объектом безопасным способом. Блокировка называется own блокируемым объектом, если он в настоящее время управляет владением этим блокируемым объектом для агента выполнения. Блокировка не управляет временем существования блокируемого объекта, на который она ссылается. [ Note: Замки предназначены для облегчения бремени разблокировки запираемого объекта как в нормальных, так и в исключительных обстоятельствах. ] end note

Некоторые конструкторы блокировок используют типы тегов, которые описывают, что следует делать с блокируемым объектом во время создания блокировки.

namespace std {
  struct defer_lock_t  { };     // do not acquire ownership of the mutex
  struct try_to_lock_t { };     // try to acquire ownership of the mutex
                                // without blocking
  struct adopt_lock_t  { };     // assume the calling thread has already
                                // obtained mutex ownership and manage it

  inline constexpr defer_lock_t   defer_lock { };
  inline constexpr try_to_lock_t  try_to_lock { };
  inline constexpr adopt_lock_t   adopt_lock { };
}

33.4.4.1 Class template lock_­guard [thread.lock.guard]

namespace std {
  template <class Mutex>
  class lock_guard {
  public:
    using mutex_type = Mutex;

    explicit lock_guard(mutex_type& m);
    lock_guard(mutex_type& m, adopt_lock_t);
    ~lock_guard();

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

  private:
    mutex_type& pm; // exposition only
  };

  template<class Mutex> lock_guard(lock_guard<Mutex>) -> lock_guard<Mutex>;
}

Объект типа lock_­guard контролирует владение блокируемым объектом в области. lock_­guard Объект сохраняет право собственности на блокируемый объект на протяжении всего lock_­guard объекта , lifetime. Поведение программы не определено, если блокируемый объект, на который указывает ссылка, pm не существует в течение всего времени существования lock_­guard объекта. Поставляемый Mutex тип должен соответствовать BasicLockable требованиям.

explicit lock_guard(mutex_type& m);

Requires: Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом m.

Effects: Как будто мимо m.lock().

Postconditions: &pm == &m

lock_guard(mutex_type& m, adopt_lock_t);

Requires: Вызывающий поток владеет мьютексом m.

Postconditions: &pm == &m

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

~lock_guard();

Effects: Как будто мимо pm.unlock().

33.4.4.2 Class template scoped_­lock [thread.lock.scoped]

namespace std {
  template <class... MutexTypes>
  class scoped_lock {
  public:
    using mutex_type = Mutex;  // If MutexTypes... consists of the single type Mutex

    explicit scoped_lock(MutexTypes&... m);
    explicit scoped_lock(MutexTypes&... m, adopt_lock_t);
    ~scoped_lock();

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

  private:
    tuple<MutexTypes&...> pm; // exposition only
  };

  template<class... MutexTypes>
    scoped_lock(scoped_lock<MutexTypes...>) -> scoped_lock<MutexTypes...>;
}

Объект типа scoped_­lock контролирует владение блокируемыми объектами в области. scoped_­lock Объект сохраняет право собственности на запираемых объектов на всей территории scoped_­lock объекта , lifetime. Поведение программы не определено, если блокируемые объекты, на которые указывает ссылка pm , не существуют в течение всего времени существования scoped_­lock объекта. Когда sizeof...(MutexTypes) есть 1, поставляемый Mutex тип должен соответствовать BasicLockable требованиям. В противном случае каждый из типов мьютексов должен соответствовать Lockable требованиям.

explicit scoped_lock(MutexTypes&... m);

Requires: Если MutexTypes тип не является рекурсивным мьютексом, вызывающий поток не владеет соответствующим элементом мьютекса m.

Effects: Инициализируется pm с помощью tie(m...). Тогда если sizeof...(MutexTypes) есть 0, то никаких эффектов. В противном случае, если sizeof...(MutexTypes) есть 1, то m.lock(). В противном случае lock(m...).

explicit scoped_lock(MutexTypes&... m, adopt_lock_t);

Requires: Вызывающий поток владеет всеми мьютексами в m.

Effects: Инициализируется pm с помощью tie(m...).

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

~scoped_lock();

Effects: Для всех i ин [0, sizeof...(MutexTypes)), get<i>(pm).unlock().

33.4.4.3 Class template unique_­lock [thread.lock.unique]

namespace std {
  template <class Mutex>
  class unique_lock {
  public:
    using mutex_type = Mutex;

    // [thread.lock.unique.cons], construct/copy/destroy
    unique_lock() noexcept;
    explicit unique_lock(mutex_type& m);
    unique_lock(mutex_type& m, defer_lock_t) noexcept;
    unique_lock(mutex_type& m, try_to_lock_t);
    unique_lock(mutex_type& m, adopt_lock_t);
    template <class Clock, class Duration>
      unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);
    template <class Rep, class Period>
      unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);
    ~unique_lock();

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

    unique_lock(unique_lock&& u) noexcept;
    unique_lock& operator=(unique_lock&& u);

    // [thread.lock.unique.locking], locking
    void lock();
    bool try_lock();

    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);

    void unlock();

    // [thread.lock.unique.mod], modifiers
    void swap(unique_lock& u) noexcept;
    mutex_type* release() noexcept;

    // [thread.lock.unique.obs], observers
    bool owns_lock() const noexcept;
    explicit operator bool () const noexcept;
    mutex_type* mutex() const noexcept;

  private:
    mutex_type* pm; // exposition only
    bool owns;      // exposition only
  };

  template<class Mutex> unique_lock(unique_lock<Mutex>) -> unique_lock<Mutex>;

  template <class Mutex>
    void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;
}

Объект типа unique_­lock контролирует владение блокируемым объектом в области. Право собственности на запираемый объект может быть приобретено во время строительства или после строительства и может быть передано после приобретения другому unique_­lock объекту. Объекты типа unique_­lock нельзя копировать, но можно перемещать. Поведение программы не определено , если содержащаяся указатель pm не является нулевым , и блокируемый объект , на который указывает pm не существует для всего оставшегося lifetime от unique_­lock объекта. Поставляемый Mutex тип должен соответствовать BasicLockable требованиям.

[ Note: unique_­lock<Mutex> соответствует BasicLockable требованиям. Если Mutex соответствует Lockable требованиям, unique_­lock<Mutex> также соответствует Lockable требованиям; если Mutex соответствует TimedLockable требованиям, unique_­lock<Mutex> также соответствует TimedLockable требованиям. ] end note

33.4.4.3.1 unique_­lock constructors, destructor, and assignment [thread.lock.unique.cons]

unique_lock() noexcept;

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

Postconditions: pm == 0 и owns == false.

explicit unique_lock(mutex_type& m);

Requires: Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом.

Effects: Создает объект типа unique_­lock и вызывает m.lock().

Postconditions: pm == addressof(m) и owns == true.

unique_lock(mutex_type& m, defer_lock_t) noexcept;

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

Postconditions: pm == addressof(m) и owns == false.

unique_lock(mutex_type& m, try_to_lock_t);

Requires: Поставляемый Mutex тип должен соответствовать Lockable требованиям. Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом.

Effects: Создает объект типа unique_­lock и вызывает m.try_­lock().

Postconditions: pm == addressof(m) и owns == res, где res - значение, возвращаемое вызовом m.try_­lock().

unique_lock(mutex_type& m, adopt_lock_t);

Requires: Вызывающий поток владеет мьютексом.

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

Postconditions: pm == addressof(m) и owns == true.

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

template <class Clock, class Duration> unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);

Requires: Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом. Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.

Effects: Создает объект типа unique_­lock и вызывает m.try_­lock_­until(abs_­time).

Postconditions: pm == addressof(m) и owns == res, где res - значение, возвращаемое вызовом m.try_­lock_­until(abs_­time).

template <class Rep, class Period> unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);

Requires: Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом. Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.

Effects: Создает объект типа unique_­lock и вызывает m.try_­lock_­for(rel_­time).

Postconditions: pm == addressof(m) и owns == res, где res - значение, возвращаемое вызовом m.try_­lock_­for(rel_­time).

unique_lock(unique_lock&& u) noexcept;

Postconditions: pm == u_­p.pm и owns == u_­p.owns (где u_­p - состояние u непосредственно перед этой конструкцией), u.pm == 0 и u.owns == false.

unique_lock& operator=(unique_lock&& u);

Effects: Если owns звонит pm->unlock().

Postconditions: pm == u_­p.pm и owns == u_­p.owns (где u_­p - состояние u непосредственно перед этой конструкцией), u.pm == 0 и u.owns == false.

[ Note: С рекурсивным мьютексом оба *this и u могут владеть одним и тем же мьютексом до назначения. В этом случае *this после присвоения мьютекс будет принадлежать и u не будет. ] end note

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

~unique_lock();

Effects: Если owns звонит pm->unlock().

33.4.4.3.2 unique_­lock locking [thread.lock.unique.locking]

void lock();

Effects: Как будто мимо pm->lock().

Postconditions: owns == true.

Throws: Любое выброшенное исключение pm->lock(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

bool try_lock();

Requires: Поставляемый Mutex должен соответствовать Lockable требованиям.

Effects: Как будто мимо pm->try_­lock().

Returns: Значение, возвращаемое вызовом try_­lock().

Postconditions: owns == res, где res - значение, возвращаемое вызовом try_­lock().

Throws: Любое выброшенное исключение pm->try_­lock(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

template <class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);

Requires: Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.

Effects: Как будто мимо pm->try_­lock_­until(abs_­time).

Returns: Значение, возвращаемое вызовом try_­lock_­until(abs_­time).

Postconditions: owns == res, где res - значение, возвращаемое вызовом try_­lock_­until(abs_­time).

Throws: Любое выброшенное исключение pm->try_­lock_­until(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);

Requires: Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.

Effects: Как будто мимо pm->try_­lock_­for(rel_­time).

Returns: Значение, возвращаемое вызовом try_­lock_­until(rel_­time).

Postconditions: owns == res, где res - значение, возвращаемое вызовом try_­lock_­for(rel_­time).

Throws: Любое выброшенное исключение pm->try_­lock_­for(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

void unlock();

Effects: Как будто мимо pm->unlock().

Postconditions: owns == false.

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

Error conditions:

  • operation_­not_­permitted - если на входе owns есть false.

33.4.4.3.3 unique_­lock modifiers [thread.lock.unique.mod]

void swap(unique_lock& u) noexcept;

Effects: Меняет местами элементы данных *this и u.

mutex_type* release() noexcept;

Returns: Предыдущее значение pm.

Postconditions: pm == 0 и owns == false.

template <class Mutex> void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;

Effects: Как будто мимо x.swap(y).

33.4.4.3.4 unique_­lock observers [thread.lock.unique.obs]

bool owns_lock() const noexcept;

Returns: owns.

explicit operator bool() const noexcept;

Returns: owns.

mutex_type *mutex() const noexcept;

Returns: pm.

33.4.4.4 Class template shared_­lock [thread.lock.shared]

namespace std {
  template <class Mutex>
  class shared_lock {
  public:
    using mutex_type = Mutex;

    // [thread.lock.shared.cons], construct/copy/destroy
    shared_lock() noexcept;
    explicit shared_lock(mutex_type& m);  // blocking
    shared_lock(mutex_type& m, defer_lock_t) noexcept;
    shared_lock(mutex_type& m, try_to_lock_t);
    shared_lock(mutex_type& m, adopt_lock_t);
    template <class Clock, class Duration>
      shared_lock(mutex_type& m,
                  const chrono::time_point<Clock, Duration>& abs_time);
    template <class Rep, class Period>
      shared_lock(mutex_type& m,
                const chrono::duration<Rep, Period>& rel_time);
    ~shared_lock();

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

    shared_lock(shared_lock&& u) noexcept;
    shared_lock& operator=(shared_lock&& u) noexcept;

    // [thread.lock.shared.locking], locking
    void lock();  // blocking
    bool try_lock();
    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    // [thread.lock.shared.mod], modifiers
    void swap(shared_lock& u) noexcept;
    mutex_type* release() noexcept;

    // [thread.lock.shared.obs], observers
    bool owns_lock() const noexcept;
    explicit operator bool () const noexcept;
    mutex_type* mutex() const noexcept;

  private:
    mutex_type* pm; // exposition only
    bool owns;      // exposition only
  };

  template<class Mutex> shared_lock(shared_lock<Mutex>) -> shared_lock<Mutex>;

  template <class Mutex>
    void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
}

Объект типа shared_­lock контролирует совместное владение блокируемым объектом в области. Совместное владение запираемым объектом может быть приобретено во время строительства или после строительства и может быть передано после приобретения другому shared_­lock объекту. Объекты типа shared_­lock нельзя копировать, но можно перемещать. Поведение программы не определено , если содержащаяся указатель pm не является нулевым , и блокируемый объект , на который указывает pm не существует для всего оставшегося lifetime от shared_­lock объекта. Поставляемый Mutex тип должен соответствовать требованиям shared mutex requirements.

[ Note: shared_­lock<Mutex> соответствует TimedLockable требованиям ([thread.req.lockable.timed]). ] end note

33.4.4.4.1 shared_­lock constructors, destructor, and assignment [thread.lock.shared.cons]

shared_lock() noexcept;

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

Postconditions: pm == nullptr и owns == false.

explicit shared_lock(mutex_type& m);

Requires: Вызывающий поток не владеет мьютексом ни в каком режиме владения.

Effects: Создает объект типа shared_­lock и вызывает m.lock_­shared().

Postconditions: pm == addressof(m) и owns == true.

shared_lock(mutex_type& m, defer_lock_t) noexcept;

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

Postconditions: pm == addressof(m) и owns == false.

shared_lock(mutex_type& m, try_to_lock_t);

Requires: Вызывающий поток не владеет мьютексом ни в каком режиме владения.

Effects: Создает объект типа shared_­lock и вызывает m.try_­lock_­shared().

Postconditions: pm == addressof(m) и owns == res где res значение, возвращаемое вызовом m.try_­lock_­shared().

shared_lock(mutex_type& m, adopt_lock_t);

Requires: Вызывающий поток имеет совместное владение мьютексом.

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

Postconditions: pm == addressof(m) и owns == true.

template <class Clock, class Duration> shared_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);

Requires: Вызывающий поток не владеет мьютексом ни в каком режиме владения.

Effects: Создает объект типа shared_­lock и вызывает m.try_­lock_­shared_­until(abs_­time).

Postconditions: pm == addressof(m) и owns == res где res значение, возвращаемое вызовом m.try_­lock_­shared_­until(abs_­time).

template <class Rep, class Period> shared_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);

Requires: Вызывающий поток не владеет мьютексом ни в каком режиме владения.

Effects: Создает объект типа shared_­lock и вызывает m.try_­lock_­shared_­for(rel_­time).

Postconditions: pm == addressof(m) и owns == res где res значение, возвращаемое вызовом m.try_­lock_­shared_­for(rel_­time).

~shared_lock();

Effects: Если owns звонит pm->unlock_­shared().

shared_lock(shared_lock&& sl) noexcept;

Postconditions: pm == sl_­p.pm и owns == sl_­p.owns (где sl_­p - состояние sl непосредственно перед этой конструкцией), sl.pm == nullptr и sl.owns == false.

shared_lock& operator=(shared_lock&& sl) noexcept;

Effects: Если owns звонит pm->unlock_­shared().

Postconditions: pm == sl_­p.pm и owns == sl_­p.owns (где sl_­p - состояние sl непосредственно перед этим назначением), sl.pm == nullptr и sl.owns == false.

33.4.4.4.2 shared_­lock locking [thread.lock.shared.locking]

void lock();

Effects: Как будто мимо pm->lock_­shared().

Postconditions: owns == true.

Throws: Любое выброшенное исключение pm->lock_­shared(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

bool try_lock();

Effects: Как будто мимо pm->try_­lock_­shared().

Returns: Значение, возвращаемое вызовом pm->try_­lock_­shared().

Postconditions: owns == res, где res - значение, возвращаемое вызовом pm->try_­lock_­shared().

Throws: Любое выброшенное исключение pm->try_­lock_­shared(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

template <class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);

Effects: Как будто мимо pm->try_­lock_­shared_­until(abs_­time).

Returns: Значение, возвращаемое вызовом pm->try_­lock_­shared_­until(abs_­time).

Postconditions: owns == res, где res - значение, возвращаемое вызовом pm->try_­lock_­shared_­until(abs_­time).

Throws: Любое выброшенное исключение pm->try_­lock_­shared_­until(abs_­time). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);

Effects: Как будто мимо pm->try_­lock_­shared_­for(rel_­time).

Returns: Значение, возвращаемое вызовом pm->try_­lock_­shared_­for(rel_­time).

Postconditions: owns == res, где res - значение, возвращаемое вызовом pm->try_­lock_­shared_­for(rel_­time).

Throws: Любое выброшенное исключение pm->try_­lock_­shared_­for(rel_­time). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

void unlock();

Effects: Как будто мимо pm->unlock_­shared().

Postconditions: owns == false.

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

Error conditions:

  • operation_­not_­permitted - если на входе owns есть false.

33.4.4.4.3 shared_­lock modifiers [thread.lock.shared.mod]

void swap(shared_lock& sl) noexcept;

Effects: Меняет местами элементы данных *this и sl.

mutex_type* release() noexcept;

Returns: Предыдущее значение pm.

Postconditions: pm == nullptr и owns == false.

template <class Mutex> void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;

Effects: Как будто мимо x.swap(y).

33.4.4.4.4 shared_­lock observers [thread.lock.shared.obs]

bool owns_lock() const noexcept;

Returns: owns.

explicit operator bool() const noexcept;

Returns: owns.

mutex_type* mutex() const noexcept;

Returns: pm.

33.4.5 Generic locking algorithms [thread.lock.algorithm]

template <class L1, class L2, class... L3> int try_lock(L1&, L2&, L3&...);

Requires: Каждый тип параметра шаблона должен соответствовать Lockable требованиям. [ Шаблон класса отвечает этим требованиям при соответствующем инстанцирован. ]Note: unique_­lock end note

Effects: Вызывает try_­lock() каждый аргумент по порядку, начиная с первого, до тех пор, пока все аргументы не будут обработаны или вызов try_­lock() не завершится ошибкой, либо путем возврата, false либо путем выброса исключения. Если вызов для try_­lock() завершается неудачно, unlock() должны вызываться все предыдущие аргументы, и не должно быть никаких дальнейших вызовов try_­lock().

Returns: -1 если все вызовы try_­lock() возвращены true, в противном случае отсчитываемое от нуля значение индекса, указывающее аргумент, для которого был try_­lock() возвращен false.

template <class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);

Requires: Каждый тип параметра шаблона должен отвечать Lockable требованиям, [ шаблонный класс отвечает этим требованиям при соответствующем инстанцирован. ]Note: unique_­lock end note

Effects: Все аргументы заблокированы через последовательность звонков lock(), try_­lock()или unlock() по каждому аргументу. Последовательность вызовов не должна приводить к тупиковой ситуации, но иначе не определена. [ Note: Должен использоваться алгоритм предотвращения взаимоблокировок, такой как попытка и откат, но конкретный алгоритм не указан, чтобы избежать чрезмерных ограничений реализаций. ] Если вызов или вызывает исключение, должен вызываться для любого аргумента, который был заблокирован вызовом или . end notelock() try_­lock() unlock() lock() try_­lock()

33.4.6 Call once [thread.once]

33.4.6.1 Struct once_­flag [thread.once.onceflag]

namespace std {
  struct once_flag {
    constexpr once_flag() noexcept;

    once_flag(const once_flag&) = delete;
    once_flag& operator=(const once_flag&) = delete;
  };
}

Класс once_­flag представляет собой непрозрачную структуру данных, которая call_­once используется для инициализации данных, не вызывая гонки данных или взаимоблокировки.

constexpr once_flag() noexcept;

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

Synchronization: Строительство once_­flag объекта не синхронизировано.

Postconditions: Внутреннее состояние объекта устанавливается таким образом, чтобы указать при вызове call_­once объекта в качестве начального аргумента, что функция не была вызвана.

33.4.6.2 Function call_­once [thread.once.callonce]

template<class Callable, class... Args> void call_once(once_flag& flag, Callable&& func, Args&&... args);

Requires:

INVOKE(std::forward<Callable>(func), std::forward<Args>(args)...)

(см. [func.require]) должно быть допустимым выражением.

Effects: Выполнение того, call_­once что не вызывает его, func есть passive казнь. Выполнение call_­once этого вызова func - это active исполнение. Активное исполнение вызовет INVOKE(​std​::​forward<Callable>(func), std​::​forward<Args>(args)...). Если такой вызов func вызывает исключение, выполняется exceptional, в противном случае - это так returning. Исключительное выполнение должно передать исключение вызывающему call_­once. Среди всех выполнений call_­once для любого данного once_­flag: не более одного должно быть повторным выполнением; если есть возвращающееся исполнение, это будет последнее активное выполнение; и есть пассивные казни, только если есть возвращающееся исполнение. [ Note: Пассивное выполнение позволяет другим потокам надежно наблюдать за результатами, полученными ранее выполненным возвратом. ] end note

Synchronization: Для любого данного once_­flag: все активные исполнения происходят в общем порядке; завершение активного выполнения synchronizes with начало следующего в этом общем порядке; и возвращающееся выполнение синхронизируется с возвратом из всех пассивных исполнений.

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

[Example:

// global flag, regular function
void init();
std::once_flag flag;

void f() {
  std::call_once(flag, init);
}

// function static flag, function object
struct initializer {
  void operator()();
};

void g() {
  static std::once_flag flag2;
  std::call_once(flag2, initializer());
}

// object flag, member function
class information {
  std::once_flag verified;
  void verifier();
public:
  void verify() { std::call_once(verified, &information::verifier, *this); }
};

end example]

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));

33.6 Futures [futures]

33.6.1 Overview [futures.overview]

[futures] описывает компоненты, которые программа на C ++ может использовать для получения в одном потоке результата (значения или исключения) от функции, которая выполнялась в том же или другом потоке. [ Note: Эти компоненты не ограничиваются многопоточными программами, но также могут быть полезны в однопоточных программах. ] end note

33.6.2 Header <future> synopsis [future.syn]

namespace std {
  enum class future_errc {
    broken_promise = implementation-defined,
    future_already_retrieved = implementation-defined,
    promise_already_satisfied = implementation-defined,
    no_state = implementation-defined
  };

  enum class launch : unspecified {
    async = unspecified,
    deferred = unspecified,
    implementation-defined
  };

  enum class future_status {
    ready,
    timeout,
    deferred
  };

  template <> struct is_error_code_enum<future_errc> : public true_type { };
  error_code make_error_code(future_errc e) noexcept;
  error_condition make_error_condition(future_errc e) noexcept;

  const error_category& future_category() noexcept;

  class future_error;

  template <class R> class promise;
  template <class R> class promise<R&>;
  template <> class promise<void>;

  template <class R>
    void swap(promise<R>& x, promise<R>& y) noexcept;

  template <class R, class Alloc>
    struct uses_allocator<promise<R>, Alloc>;

  template <class R> class future;
  template <class R> class future<R&>;
  template <> class future<void>;

  template <class R> class shared_future;
  template <class R> class shared_future<R&>;
  template <> class shared_future<void>;

  template <class> class packaged_task;   // not defined
  template <class R, class... ArgTypes>
    class packaged_task<R(ArgTypes...)>;

  template <class R, class... ArgTypes>
    void swap(packaged_task<R(ArgTypes...)>&, packaged_task<R(ArgTypes...)>&) noexcept;

  template <class R, class Alloc>
    struct uses_allocator<packaged_task<R>, Alloc>;

  template <class F, class... Args>
    future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
    async(F&& f, Args&&... args);
  template <class F, class... Args>
    future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
    async(launch policy, F&& f, Args&&... args);
}

enum Типа launch является bitmask type с элементами launch​::​async и launch​::​deferred. [ Note: Реализации могут предоставлять битовые маски для указания ограничений на взаимодействие задач с помощью функций, запускаемых async() применимыми к соответствующему подмножеству доступных политик запуска. Реализации могут расширять поведение первой перегрузки async() , добавляя свои расширения в политику запуска в соответствии с правилом «как если бы». ] end note

Значения перечисления future_­errc различны и не равны нулю.

33.6.3 Error handling [futures.errors]

const error_category& future_category() noexcept;

Returns:  Ссылка на объект типа, производного от класса error_­category.

Объектные default_­error_­condition и эквивалентные виртуальные функции должны вести себя, как указано для класса error_­category. name Виртуальная функция объекта должна возвращать указатель на строку "future".

error_code make_error_code(future_errc e) noexcept;

Returns: error_­code(static_­cast<int>(e), future_­category()).

error_condition make_error_condition(future_errc e) noexcept;

Returns: error_­condition(static_­cast<int>(e), future_­category()).

33.6.4 Class future_­error [futures.future_error]

namespace std {
  class future_error : public logic_error {
  public:
    explicit future_error(future_errc e);

    const error_code& code() const noexcept;
    const char*       what() const noexcept;
  private:
    error_code ec_;  // exposition only
  };
}

explicit future_error(future_errc e);

Effects: Создает объект класса future_­error и инициализируется ec_­ с помощью make_­error_­code(e).

const error_code& code() const noexcept;

Returns: ec_­.

const char* what() const noexcept;

Returns: An НТБ включения code().message().

33.6.5 Shared state [futures.state]

Многие из классов, представленных в этом подпункте, используют некоторое состояние для передачи результатов. Он shared state состоит из некоторой информации о состоянии и некоторой информации (возможно, еще не оцененной) result, которая может быть (возможно, недействительной) значением или исключением. [ Note: Фьючерсы, обещания и задачи, определенные в этом пункте, ссылаются на такое совместно используемое состояние. ] end note

[ Note: Результатом может быть любой объект, включая функцию для вычисления этого результата, как это используется async when policy is launch​::​deferred. ] end note

An asynchronous return object - это объект, который считывает результаты из общего состояния. Объект waiting function асинхронного возврата - это объект, который потенциально блокирует ожидание готовности общего состояния. Если ожидающая функция может вернуться до того, как состояние будет подготовлено из-за тайм-аута ([thread.req.lockable]), то это a timed waiting function, в противном случае - non-timed waiting function.

An asynchronous provider - это объект, который предоставляет результат для общего состояния. Результат общего состояния устанавливается соответствующими функциями асинхронного провайдера. [ Note: Например, обещания или задачи. ] Средства установки результата общего состояния указаны в описании тех классов и функций, которые создают такой объект состояния.end note

Когда говорится, что асинхронный возвращаемый объект или асинхронный провайдер освобождает свое общее состояние, это означает:

  • если возвращаемый объект или поставщик содержит последнюю ссылку на его совместно используемое состояние, совместно используемое состояние уничтожается; а также

  • возвращаемый объект или поставщик отказывается от ссылки на свое общее состояние; а также

  • эти действия не будут блокировать общее состояние, чтобы стать готовым, за исключением того, что они могут блокироваться, если выполняются все следующие условия: общее состояние было создано вызовом std​::​async, общее состояние еще не готово, и это была последняя ссылка в общее состояние.

Когда говорят, что асинхронный провайдер готовит свое общее состояние, это означает:

  • во-первых, провайдер отмечает свое общее состояние как готовое; а также

  • во-вторых, провайдер разблокирует все агенты выполнения, ожидающие готовности его общего состояния.

Когда говорится, что асинхронный провайдер отказался от своего общего состояния, это означает:

  • во-первых, если это состояние не готово, провайдер

    • сохраняет объект исключения типа future_­error с условием ошибки в broken_­promise пределах его общего состояния; а потом

    • делает свое разделяемое состояние готовым;

  • во-вторых, провайдер освобождает свое общее состояние.

Общее состояние возможно ready только в том случае, если оно содержит значение или исключение, готовое к извлечению. Ожидание готовности общего состояния может вызвать код для вычисления результата в ожидающем потоке, если это указано в описании класса или функции, создающей объект состояния.

Вызов функций, которые успешно устанавливают сохраненный результат общего состояния, synchronize with вызывает функции, успешно обнаруживающие состояние готовности, являющееся результатом этого параметра. Сохранение результата (нормального или исключительного) в совместно используемом состоянии synchronizes with - успешный возврат из вызова функции ожидания в совместно используемом состоянии.

Некоторые функции (например promise​::​set_­value_­at_­thread_­exit) задерживают подготовку общего состояния до завершения вызывающего потока. Уничтожение каждого из объектов этого потока с помощью thread storage duration упорядочивается до того, как это общее состояние будет готово.

Доступ к результату того же общего состояния может conflict. [ Note: Это явно указывает, что результат общего состояния виден в объектах, которые ссылаются на это состояние в смысле data race avoidance. Например, одновременный доступ через ссылки, возвращаемые shared_­future​::​get() ([futures.shared_future]), должен использовать операции только для чтения или обеспечивать дополнительную синхронизацию. ]end note

33.6.6 Class template promise [futures.promise]

namespace std {
  template <class R>
  class promise {
  public:
    promise();
    template <class Allocator>
      promise(allocator_arg_t, const Allocator& a);
    promise(promise&& rhs) noexcept;
    promise(const promise& rhs) = delete;
    ~promise();

    // assignment
    promise& operator=(promise&& rhs) noexcept;
    promise& operator=(const promise& rhs) = delete;
    void swap(promise& other) noexcept;

    // retrieving the result
    future<R> get_future();

    // setting the result
    void set_value(see below);
    void set_exception(exception_ptr p);

    // setting the result with deferred notification
    void set_value_at_thread_exit(see below);
    void set_exception_at_thread_exit(exception_ptr p);
  };
  template <class R>
    void swap(promise<R>& x, promise<R>& y) noexcept;
  template <class R, class Alloc>
    struct uses_allocator<promise<R>, Alloc>;
}

Реализация должна предоставлять шаблон promise и две специализации, promise<R&> и promise<​void>. Они различаются только типом аргумента функций-членов set_­value и set_­value_­at_­thread_­exit, как указано в их описаниях ниже.

set_­value, set_­exception, set_­value_­at_­thread_­exit, И set_­exception_­at_­thread_­exit функция - членов ведут себя так , как будто они приобретают один семафор , связанный с объектом обещания при обновлении объекта обещания.

template <class R, class Alloc> struct uses_allocator<promise<R>, Alloc> : true_type { };

Requires: Alloc должен быть Allocator.

promise(); template <class Allocator> promise(allocator_arg_t, const Allocator& a);

Effects: создает promise объект и общее состояние. Второй конструктор использует распределитель a для выделения памяти для общего состояния.

promise(promise&& rhs) noexcept;

Effects: создает новый promise объект и передает право собственности на совместно используемое состояние rhs (если таковое имеется) на вновь созданный объект.

Postconditions: rhs не имеет общего состояния.

~promise();

Effects: Отменяет любое общее состояние ([futures.state]).

promise& operator=(promise&& rhs) noexcept;

Effects: Отменяет любое разделяемое состояние ([futures.state]), а затем как если бы promise(std​::​move(rhs)).swap(*this).

Returns: *this.

void swap(promise& other) noexcept;

Effects: Меняет общее состояние *this и other.

Postconditions: *this имеет общее состояние (если есть), которое other было до вызова swap. other имеет общее состояние (если есть), которое *this было до вызова swap.

future<R> get_future();

Returns: future<R> Объект с тем же общим состоянием как *this.

Throws: future_­error если *this не имеет общего состояния или если get_­future уже был вызван в promise с тем же общим состоянием, что и *this.

Error conditions:

  • future_­already_­retrieved if get_­future уже был вызван promise с тем же общим состоянием, что и *this.

  • no_­state если *this не имеет общего состояния.

void promise::set_value(const R& r); void promise::set_value(R&& r); void promise<R&>::set_value(R& r); void promise<void>::set_value();

Effects: Атомарно сохраняет значение r в общем состоянии и делает это состояние готовым ([futures.state]).

Throws:

  • future_­error если его общее состояние уже имеет сохраненное значение или исключение, или

  • для первой версии любое исключение, созданное конструктором, выбранным для копирования объекта R, или

  • для второй версии любое исключение, созданное конструктором, выбранным для перемещения объекта R.

Error conditions:

  • promise_­already_­satisfied если его общее состояние уже имеет сохраненное значение или исключение.

  • no_­state если *this не имеет общего состояния.

void set_exception(exception_ptr p);

Requires: p не равно нулю.

Effects: Атомарно сохраняет указатель исключения p в общем состоянии и делает это состояние готовым ([futures.state]).

Throws: future_­error если его общее состояние уже имеет сохраненное значение или исключение.

Error conditions:

  • promise_­already_­satisfied если его общее состояние уже имеет сохраненное значение или исключение.

  • no_­state если *this не имеет общего состояния.

void promise::set_value_at_thread_exit(const R& r); void promise::set_value_at_thread_exit(R&& r); void promise<R&>::set_value_at_thread_exit(R& r); void promise<void>::set_value_at_thread_exit();

Effects: Сохраняет значение r в общем состоянии, не делая это состояние немедленно готовым. Планирует, что состояние будет подготовлено к выходу из текущего потока после того, как все объекты продолжительности хранения потока, связанные с текущим потоком, будут уничтожены.

Throws:

  • future_­error если его общее состояние уже имеет сохраненное значение или исключение, или

  • для первой версии любое исключение, созданное конструктором, выбранным для копирования объекта R, или

  • для второй версии любое исключение, созданное конструктором, выбранным для перемещения объекта R.

Error conditions:

  • promise_­already_­satisfied если его общее состояние уже имеет сохраненное значение или исключение.

  • no_­state если *this не имеет общего состояния.

void set_exception_at_thread_exit(exception_ptr p);

Requires: p не равно нулю.

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

Throws: future_­error если возникает состояние ошибки.

Error conditions:

  • promise_­already_­satisfied если его общее состояние уже имеет сохраненное значение или исключение.

  • no_­state если *this не имеет общего состояния.

template <class R> void swap(promise<R>& x, promise<R>& y) noexcept;

Effects: Как будто мимо x.swap(y).

33.6.7 Class template future [futures.unique_future]

Шаблон класса future определяет тип для асинхронных возвращаемых объектов, которые не разделяют свое общее состояние с другими асинхронными возвращаемыми объектами. Созданный по умолчанию future объект не имеет общего состояния. future Объект с общим состоянием может быть создан с помощью функций на asynchronous providers или с помощью конструктора перемещения и разделяет его общее состояние с первоначальным асинхронным поставщиком. Результат (значение или исключение) future объекта может быть установлен путем вызова соответствующей функции для объекта, который имеет то же общее состояние.

[ Note: Функции-члены future не синхронизируются сами с собой или с функциями-членами shared_­future. ] end note

Эффект вызова какой - либо функции - члена, кроме деструктора, оператор присваивания-ход, shareили valid на future объекте , для которого valid() == false не определено. [ Note: Действительно перейти от будущего объекта, для которого valid() == false. ] [ Реализациям рекомендуется обнаруживать этот случай и генерировать объект типа с условием ошибки . ]end noteNote: future_­error future_­errc​::​no_­state end note

namespace std {
  template <class R>
  class future {
  public:
    future() noexcept;
    future(future&&) noexcept;
    future(const future& rhs) = delete;
    ~future();
    future& operator=(const future& rhs) = delete;
    future& operator=(future&&) noexcept;
    shared_future<R> share() noexcept;

    // retrieving the value
    see below get();

    // functions to check state
    bool valid() const noexcept;

    void wait() const;
    template <class Rep, class Period>
      future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
    template <class Clock, class Duration>
      future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
  };
}

Реализация должна предоставлять шаблон future и две специализации, future<R&> и future<​void>. Они отличаются только типом возвращаемого значения и возвращаемым значением функции-члена get, как указано в ее описании ниже.

future() noexcept;

Effects: Создает empty future объект, который не относится к общему состоянию.

Postconditions: valid() == false.

future(future&& rhs) noexcept;

Effects: Move создает future объект, который ссылается на общее состояние, на которое первоначально ссылался rhs (если таковой имеется).

Postconditions:

  • valid() возвращает то же значение, что и rhs.valid() до вызова конструктора.

  • rhs.valid() == false.

~future();

Effects:

  • Освобождает любое разделяемое состояние ([futures.state]);

  • разрушает *this.

future& operator=(future&& rhs) noexcept;

Effects:

  • Освобождает любое общее состояние ([futures.state]).

  • переместить Назначает содержимое rhs в *this.

Postconditions:

  • valid() возвращает то же значение, что и rhs.valid() до присвоения.

  • rhs.valid() == false.

shared_future<R> share() noexcept;

Returns: shared_­future<R>(std​::​move(*this)).

Postconditions: valid() == false.

R future::get(); R& future<R&>::get(); void future<void>::get();

[ Note: Как описано выше, шаблон и две его требуемые специализации отличаются только типом возвращаемого значения и возвращаемым значением функции-члена get. ]end note

Effects:

  • wait()s, пока общее состояние не будет готово, затем извлекает значение, хранящееся в общем состоянии;

  • освобождает любое разделяемое состояние ([futures.state]).

Returns:

  • future​::​get() возвращает значение, v хранящееся в общем состоянии объекта как std​::​move(v).

  • future<R&>​::​get() возвращает ссылку, хранящуюся как значение в общем состоянии объекта.

  • future<void>​::​get() ничего не возвращает.

Throws: сохраненное исключение, если исключение было сохранено в общем состоянии.

Postconditions: valid() == false.

bool valid() const noexcept;

Returns: true только если *this относится к общему состоянию.

void wait() const;

Effects: Блокируется, пока не будет готово общее состояние.

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

Effects: Нет, если общее состояние содержит отложенную функцию ([futures.async]), в противном случае блокируется, пока общее состояние не будет готово или пока не истечет относительный тайм-аут ([thread.req.timing]), указанный в rel_­time .

Returns:

  • future_­status​::​deferred если общее состояние содержит отложенную функцию.

  • future_­status​::​ready если общее состояние готово.

  • future_­status​::​timeout если функция возвращается из-за истечения относительного тайм-аута ([thread.req.timing]), указанного в rel_­time .

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

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

Effects: Нет, если общее состояние содержит отложенную функцию ([futures.async]), в противном случае блокируется, пока общее состояние не будет готово или пока не истечет абсолютный тайм-аут ([thread.req.timing]), указанный в abs_­time .

Returns:

  • future_­status​::​deferred если общее состояние содержит отложенную функцию.

  • future_­status​::​ready если общее состояние готово.

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

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

33.6.8 Class template shared_­future [futures.shared_future]

Шаблон класса shared_­future определяет тип для асинхронных возвращаемых объектов, которые могут совместно использовать свое общее состояние с другими асинхронными возвращаемыми объектами. Созданный по умолчанию shared_­future объект не имеет общего состояния. shared_­future Объект с общим состоянием может быть создан путем преобразования из future объекта и разделяет его общее состояние с оригиналом asynchronous provider разделяемым государством. Результат (значение или исключение) shared_­future объекта может быть установлен путем вызова соответствующей функции для объекта, который имеет то же общее состояние.

[ Note: Функции-члены shared_­future не синхронизируются сами с собой, но синхронизируются с общим состоянием. ] end note

Эффект вызова какой - либо функция - член, кроме деструктора, оператор шага присваивания, оператор копирования назначения, или valid() на shared_­future объекте , для которого valid() == false не определен. [ Note: Допустимо копировать или перемещать shared_­future объект, для которого valid() есть false. ] [ Реализациям рекомендуется обнаруживать этот случай и генерировать объект типа с условием ошибки . ] end noteNote: future_­error future_­errc​::​no_­state end note

namespace std {
  template <class R>
  class shared_future {
  public:
    shared_future() noexcept;
    shared_future(const shared_future& rhs) noexcept;
    shared_future(future<R>&&) noexcept;
    shared_future(shared_future&& rhs) noexcept;
    ~shared_future();
    shared_future& operator=(const shared_future& rhs) noexcept;
    shared_future& operator=(shared_future&& rhs) noexcept;

    // retrieving the value
    see below get() const;

    // functions to check state
    bool valid() const noexcept;

    void wait() const;
    template <class Rep, class Period>
      future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
    template <class Clock, class Duration>
      future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
  };
}

Реализация должна предоставлять шаблон shared_­future и две специализации, shared_­future<R&> и shared_­future<void>. Они отличаются только типом возвращаемого значения и возвращаемым значением функции-члена get, как указано в ее описании ниже.

shared_future() noexcept;

Effects: Создает empty shared_­future объект, который не относится к общему состоянию.

Postconditions: valid() == false.

shared_future(const shared_future& rhs) noexcept;

Effects: Создает shared_­future объект, который ссылается на то же общее состояние, что и rhs (если есть).

Postconditions: valid() возвращает то же значение, что и rhs.valid().

shared_future(future<R>&& rhs) noexcept; shared_future(shared_future&& rhs) noexcept;

Effects: Move создает shared_­future объект, который ссылается на общее состояние, на которое первоначально ссылался rhs (если таковой имеется).

Postconditions:

  • valid() возвращает то же значение, которое было rhs.valid() возвращено до вызова конструктора.

  • rhs.valid() == false.

~shared_future();

Effects:

shared_future& operator=(shared_future&& rhs) noexcept;

Effects:

  • Освобождает любое разделяемое состояние ([futures.state]);

  • переместить Назначает содержимое rhs в *this.

Postconditions:

  • valid() возвращает то же значение, которое было rhs.valid() возвращено до присвоения.

  • rhs.valid() == false.

shared_future& operator=(const shared_future& rhs) noexcept;

Effects:

  • Освобождает любое разделяемое состояние ([futures.state]);

  • присваивает содержимое rhs в *this. [ Note: В результате *this относится к тому же общему состоянию, что и rhs (если есть). ] end note

Postconditions: valid() == rhs.valid().

const R& shared_future::get() const; R& shared_future<R&>::get() const; void shared_future<void>::get() const;

[ Note: Как описано выше, шаблон и две его требуемые специализации отличаются только типом возвращаемого значения и возвращаемым значением функции-члена get. ]end note

[ Note: Доступ к объекту значения, хранящемуся в общем состоянии, несинхронизирован, поэтому программисты должны применять только те операции, над R которыми не возникает гонка данных ([intro.multithread]). ]end note

Effects: wait()s, пока общее состояние не будет готово, затем извлекает значение, хранящееся в общем состоянии.

Returns:

  • shared_­future​::​get() возвращает константную ссылку на значение, хранящееся в общем состоянии объекта. [ Note: Доступ по этой ссылке после уничтожения общего состояния приводит к неопределенному поведению; этого можно избежать, если не хранить ссылку в любом хранилище с большим сроком жизни, чем у shared_­future объекта, который вернул ссылку. ] end note

  • shared_­future<R&>​::​get() возвращает ссылку, хранящуюся как значение в общем состоянии объекта.

  • shared_­future<void>​::​get() ничего не возвращает.

Throws: сохраненное исключение, если исключение было сохранено в общем состоянии.

bool valid() const noexcept;

Returns: true только если *this относится к общему состоянию.

void wait() const;

Effects: Блокируется, пока не будет готово общее состояние.

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

Effects: Нет, если общее состояние содержит отложенную функцию ([futures.async]), в противном случае блокируется, пока общее состояние не будет готово или пока не истечет относительный тайм-аут ([thread.req.timing]), указанный в rel_­time .

Returns:

  • future_­status​::​deferred если общее состояние содержит отложенную функцию.

  • future_­status​::​ready если общее состояние готово.

  • future_­status​::​timeout если функция возвращается из-за истечения относительного тайм-аута ([thread.req.timing]), указанного в rel_­time .

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

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

Effects: Нет, если общее состояние содержит отложенную функцию ([futures.async]), в противном случае блокируется, пока общее состояние не будет готово или пока не истечет абсолютный тайм-аут ([thread.req.timing]), указанный в abs_­time .

Returns:

  • future_­status​::​deferred если общее состояние содержит отложенную функцию.

  • future_­status​::​ready если общее состояние готово.

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

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

33.6.9 Function template async [futures.async]

Шаблон функции async предоставляет механизм для запуска функции потенциально в новом потоке и предоставляет результат функции в future объекте, с которым он разделяет общее состояние.

template <class F, class... Args> future<invoke_result_t<decay_t<F>, decay_t<Args>...>> async(F&& f, Args&&... args); template <class F, class... Args> future<invoke_result_t<decay_t<F>, decay_t<Args>...>> async(launch policy, F&& f, Args&&... args);

Requires: F и каждый Ti в Args должен удовлетворять MoveConstructible требованиям, и

INVOKE(DECAY_COPY(std::forward<F>(f)),
       DECAY_COPY(std::forward<Args>(args))...)     // see [func.require], [thread.thread.constr]

должно быть допустимым выражением.

Effects: Первая функция ведет себя так же, как вызов второй функции с policy аргументом launch​::​async | launch​::​deferred и теми же аргументами для F и Args. Вторая функция создает общее состояние, связанное с возвращаемым future объектом. Дальнейшее поведение второй функции зависит от policy аргумента следующим образом (если применяется более одного из этих условий, реализация может выбрать любую из соответствующих политик):

  • Если launch​::​async установлено policy, вызывает INVOKE(DECAY_­COPY(std​::​forward<F>(f)), DECAY_­COPY(std​::​forward<Args>(args))...) ([func.require], [thread.thread.constr]), как если бы в новом потоке выполнения, представленном thread объектом, с вызовами DECAY_­COPY() , оцениваемыми в вызвавшем потоке async. Любое возвращаемое значение сохраняется как результат в общем состоянии. Любое исключение, возникшее в результате выполнения, INVOKE(DECAY_­COPY(std​::​forward<F>(f)), DECAY_­COPY(std​::​forward<Args>(args))...) сохраняется как исключительный результат в общем состоянии. thread Объект хранится в совместно используемом состоянии и влияет на поведение любого асинхронного возврата объекты , которые ссылаются , что состояния.

  • Если launch​::​deferred установлено policy, хранит DECAY_­COPY(std​::​forward<F>(f)) и DECAY_­COPY(std​::​forward<Args>(args))... в общем состоянии. Эти копии f и args составляют deferred function. Вызов отложенной функции оценивает, INVOKE(std​::​move(g), std​::​move(xyz)) где g находится сохраненное значение, DECAY_­COPY(std​::​forward<F>(f)) а где xyz - сохраненная копия DECAY_­COPY(std​::​forward<Args>(args)).... Любое возвращаемое значение сохраняется как результат в общем состоянии. Любое исключение, возникшее в результате выполнения отложенной функции, сохраняется как исключительный результат в общем состоянии. Общее состояние не становится готовым, пока функция не завершится. Первый вызов функции ожидания без времени ([futures.state]) для асинхронного возвращаемого объекта, ссылающегося на это совместно используемое состояние, должен вызывать отложенную функцию в потоке, который вызвал функцию ожидания. После начала оценки INVOKE(std​::​move(g), std​::​move(xyz)) функция больше не считается отложенной. [ Note: Если эта политика указана вместе с другими политиками, например, при использовании policy значения launch​::​async | launch​::​deferred, реализации должны отложить вызов или выбор политики, когда параллелизм больше не может быть эффективно использован. ] end note

  • Если в политике запуска не задано значение или установлено значение, которое не указано ни в этом международном стандарте, ни в реализации, поведение не определено.

Returns: Объект типа, future<invoke_­result_­t<decay_­t<F>, decay_­t<Args>...>> который ссылается на общее состояние, созданное этим вызовом async. [ Note: Если будущее, полученное из async , перемещается за пределы локальной области видимости, другой код, который использует будущее, должен знать, что деструктор будущего может заблокироваться, чтобы общее состояние стало готовым. ] end note

Synchronization: Независимо от приведенного policy аргумента,

  • призыв async synchronizes with призывания f. [ Note: Это утверждение применяется, даже когда соответствующий future объект перемещается в другой поток. ]; а также end note

  • завершение функции f происходит до ([intro.multithread]) готовности общего состояния. [ Note: f может вообще не вызываться, поэтому его завершение может никогда не произойти. ] end note

Если реализация выбирает launch​::​async политику,

  • вызов функции ожидания на асинхронном возвращаемом объекте, который разделяет общее состояние, созданное этим async вызовом, должен блокироваться до тех пор, пока связанный поток не завершится, как если бы он был присоединен, или иначе истечет время ожидания ([thread.thread.member]);

  • связанный поток завершает synchronizes with возврат из первой функции, которая успешно определяет состояние готовности совместно используемого состояния, или возвратом из последней функции, которая освобождает совместно используемое состояние, в зависимости от того, что произойдет раньше.

Throws: system_­error если policy == launch​::​async и реализация не может запустить новый поток, или std​::​bad_­alloc если память для внутренних структур данных не может быть выделена.

Error conditions:

  • resource_­unavailable_­try_­again - если policy == launch​::​async и система не может начать новый поток.

[Example:

int work1(int value);
int work2(int value);
int work(int value) {
  auto handle = std::async([=]{ return work2(value); });
  int tmp = work1(value);
  return tmp + handle.get();    // #1
}

[ Note: Строка №1 может не привести к параллелизму, потому что async вызов использует политику по умолчанию, которая может использовать launch​::​deferred, и в этом случае лямбда может не вызываться до get() вызова; в этом случае work1 и work2 вызываются в одном потоке, и параллелизма нет. ] ]end noteend example

33.6.10 Class template packaged_­task [futures.task]

Шаблон класса packaged_­task определяет тип оболочки для функции или вызываемого объекта, чтобы возвращаемое значение функции или вызываемого объекта сохранялось в будущем при его вызове.

Когда packaged_­task объект вызывается, вызывается его сохраненная задача, а результат (нормальный или исключительный) сохраняется в общем состоянии. Любые фьючерсы, которые разделяют общее состояние, затем смогут получить доступ к сохраненному результату.

namespace std {
  template<class> class packaged_task; // not defined

  template<class R, class... ArgTypes>
  class packaged_task<R(ArgTypes...)> {
  public:
    // construction and destruction
    packaged_task() noexcept;
    template <class F>
      explicit packaged_task(F&& f);
    ~packaged_task();

    // no copy
    packaged_task(const packaged_task&) = delete;
    packaged_task& operator=(const packaged_task&) = delete;

    // move support
    packaged_task(packaged_task&& rhs) noexcept;
    packaged_task& operator=(packaged_task&& rhs) noexcept;
    void swap(packaged_task& other) noexcept;

    bool valid() const noexcept;

    // result retrieval
    future<R> get_future();

    // execution
    void operator()(ArgTypes... );
    void make_ready_at_thread_exit(ArgTypes...);

    void reset();
  };
  template <class R, class... ArgTypes>
    void swap(packaged_task<R(ArgTypes...)>& x, packaged_task<R(ArgTypes...)>& y) noexcept;
  template <class R, class Alloc>
    struct uses_allocator<packaged_task<R>, Alloc>;
}

33.6.10.1 packaged_­task member functions [futures.task.members]

packaged_task() noexcept;

Effects: Создает packaged_­task объект без общего состояния и без сохраненной задачи.

template <class F> packaged_task(F&& f);

Requires: INVOKE<R>(f, t1, t2, ..., tN), где t1, t2, ..., tN - значения соответствующих типов в ArgTypes..., должно быть допустимым выражением. Вызов копии f должен вести себя так же, как и вызов f.

Remarks: Этот конструктор не должен участвовать в разрешении перегрузки, если decay_­t<F> он того же типа, что и packaged_­task<R(ArgTypes...)>.

Effects: Создает новый packaged_­task объект с общим состоянием и инициализирует сохраненную задачу объекта с помощью std​::​forward<F>(f).

Throws:

  • Любые исключения, создаваемые конструктором копирования или перемещения f.

  • Для первой версии, bad_­alloc если не удалось выделить память для внутренних структур данных.

  • Для второй версии любые исключения, созданные allocator_­traits<Allocator>​::​template rebind_­traits<unspecified>​::​allocate.

packaged_task(packaged_task&& rhs) noexcept;

Effects: Создает новый packaged_­task объект и передает владение rhsобщим состоянием *this, оставив его rhs без общего состояния. Перемещает сохраненную задачу из rhs в *this.

Postconditions: rhs не имеет общего состояния.

packaged_task& operator=(packaged_task&& rhs) noexcept;

Effects:

  • Освобождает любое разделяемое состояние ([futures.state]);

  • звонки packaged_­task(std​::​move(rhs)).swap(*this).

~packaged_task();

Effects: Отменяет любое общее состояние ([futures.state]).

void swap(packaged_task& other) noexcept;

Effects: Меняет общие состояния и сохраненные задачи *this и other.

Postconditions: *this имеет такое же общее состояние и сохраненную задачу (если есть), что и other до вызова swap. other имеет такое же общее состояние и сохраненную задачу (если есть), что и *this до вызова swap.

bool valid() const noexcept;

Returns: true только если *this есть общее состояние.

future<R> get_future();

Returns: future Объект , который разделяет то же общегосударственный как *this.

Throws: future_­error объекта в случае возникновения ошибки.

Error conditions:

  • future_­already_­retrieved if get_­future уже был вызван для packaged_­task объекта с тем же общим состоянием, что и *this.

  • no_­state если *this не имеет общего состояния.

void operator()(ArgTypes... args);

Effects: Как будто по INVOKE<R>(f, t1, t2, ..., tN), где f хранится задача *this и t1, t2, ..., tN значения в args.... Если задача возвращается нормально, возвращаемое значение сохраняется как асинхронный результат в общем состоянии *this, в противном случае сохраняется исключение, созданное задачей. Общее состояние *this готово, и все потоки, заблокированные в функции, ожидающие *this готовности общего состояния , разблокируются.

Throws: future_­error объект исключения , если нет общегосударственных или сохраненное задание уже вызван.

Error conditions:

  • promise_­already_­satisfied если сохраненная задача уже была вызвана.

  • no_­state если *this не имеет общего состояния.

void make_ready_at_thread_exit(ArgTypes... args);

Effects: Как будто по INVOKE<R>(f, t1, t2, ..., tN), где f хранится задача, а t1, t2, ..., tN какие - значения в args.... Если задача возвращается нормально, возвращаемое значение сохраняется как асинхронный результат в общем состоянии *this, в противном случае сохраняется исключение, созданное задачей. В любом случае это должно быть сделано без[futures.state]немедленного преобразования этого состояния в состояние ready ( ). Планирует подготовить общее состояние к моменту выхода из текущего потока после того, как все объекты продолжительности хранения потока, связанные с текущим потоком, будут уничтожены.

Throws: future_­error если возникает состояние ошибки.

Error conditions:

  • promise_­already_­satisfied если сохраненная задача уже была вызвана.

  • no_­state если *this не имеет общего состояния.

void reset();

Effects: Как будто *this = packaged_­task(std​::​move(f)), где f хранится задача *this. [ Note: Это создает новое общее состояние для *this. Старое состояние заброшено ([futures.state]). ] end note

Throws:

  • bad_­alloc если не удалось выделить память для нового общего состояния.

  • любое исключение, созданное конструктором перемещения задачи, хранящейся в общем состоянии.

  • future_­error с ошибкой no_­state if *this не имеет общего состояния.

33.6.10.2 packaged_­task globals [futures.task.nonmembers]

template <class R, class... ArgTypes> void swap(packaged_task<R(ArgTypes...)>& x, packaged_task<R(ArgTypes...)>& y) noexcept;

Effects: Как будто мимо x.swap(y).

template <class R, class Alloc> struct uses_allocator<packaged_task<R>, Alloc> : true_type { };

Requires: Alloc должен быть Allocator.