В следующих подпунктах описываются компоненты для создания и управления threads, выполнения взаимного исключения и передачи условий и значений между потоками, как показано в таблице 140.
Подпункт | Заголовок (ы) | |
[thread.req] | Требования | |
[thread.threads] | Потоки | <thread> |
[thread.mutex] | Взаимное исключение | <mutex> |
<shared_mutex> | ||
[thread.condition] | Переменные условия | <condition_variable> |
[futures] | Фьючерсы | <future> |
Некоторые функции, описанные в этом разделе, указаны для создания исключений определенного типа 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
Некоторые классы, описанные в этом разделе, имеют члены native_handle_type и native_handle. Наличие этих членов и их семантика определяется реализацией. [ Note: Эти члены позволяют реализациям предоставлять доступ к деталям реализации. Их имена указаны для облегчения обнаружения переносимого времени компиляции. Фактическое использование этих членов по своей природе непереносимо. ] — end note
Некоторые функции, описанные в этом разделе, принимают аргумент для указания тайм-аута. Эти тайм-ауты указываются либо как, либо 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
Все реализации, для которых значимы стандартные единицы времени, обязательно должны иметь устойчивые часы в своей аппаратной реализации.
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
Тип L соответствует BasicLockable требованиям, если следующие выражения правильно сформированы и имеют указанную семантику (m обозначает значение типа L).
m.lock()
Effects: Блокируется до тех пор, пока для текущего агента выполнения не будет получена блокировка. Если выбрасывается исключение, то для текущего агента выполнения блокировка не должна быть получена.
m.unlock()
Тип L соответствует Lockable требованиям, если он соответствует BasicLockable требованиям, а следующие выражения правильно сформированы и имеют указанную семантику (m обозначает значение типа L).
m.try_lock()
Effects: Пытается получить блокировку для текущего агента выполнения без блокировки. Если выбрасывается исключение, то для текущего агента выполнения блокировка не должна быть получена.
Тип 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 для текущего агента выполнения. Если выбрасывается исключение, то для текущего агента выполнения блокировка не должна быть получена.
m.try_lock_until(abs_time)
Effects: Пытается получить блокировку для текущего агента выполнения до абсолютного тайм-аута ([thread.req.timing]), указанного в abs_time. Функция не должна возвращаться до истечения тайм-аута, указанного в, abs_time если только она не получила блокировку m для текущего агента выполнения. Если выбрасывается исключение, то для текущего агента выполнения блокировка не должна быть получена.
[thread.threads] описывает компоненты, которые можно использовать для создания потоков и управления ими. [ Note: Эти потоки предназначены для взаимно-однозначного сопоставления с потоками операционной системы. ] — end note
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); } }
Класс 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; }; }
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;
bool operator==(thread::id x, thread::id y) noexcept;
Returns: true только в том случае, если x и y представляют один и тот же поток выполнения или не представляют x ни y один поток выполнения.
bool operator!=(thread::id x, thread::id y) noexcept;
bool operator<(thread::id x, thread::id y) noexcept;
Returns: Такое значение, которое operator< представляет собой общий порядок, как описано в [alg.sorting].
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);
Effects: Вставляет неопределенное текстовое представление id в out. Для двух объектов типа thread::id x и y, если x == y эти thread::id объекты будут иметь то же текстовое представление , и если x != y эти thread::id объекты имеют различные текстовые представления.
template <> struct hash<thread::id>;
Специализация включена ([unord.hash]).
thread() noexcept;
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 note INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...) terminate
thread(thread&& x) noexcept;
Effects: Создает объект типа thread из xи устанавливает x в сконструированное состояние по умолчанию.
~thread();
Если joinable(), звонит terminate(). В противном случае не имеет никакого эффекта. [ Note: Неявное отсоединение или присоединение к joinable() потоку в его деструкторе может привести к трудностям в отладке ошибок корректности (для отсоединения) или производительности (для присоединения), обнаруживаемых только при возникновении исключения. Таким образом, программист должен гарантировать, что деструктор никогда не будет выполняться, пока поток все еще может быть присоединен. ] — end note
thread& operator=(thread&& x) noexcept;
Effects: Если joinable(), звонит terminate(). В противном случае, присваивает состояние , x чтобы *this и множеств x в состояние по умолчанию строится.
void swap(thread& x) noexcept;
bool joinable() const noexcept;
void join();
Synchronization: Завершение потока, представленное *this synchronizes with соответствующим успешным join() возвратом. [ Note: Операции на *this не синхронизированы. ] — end note
Throws: system_error когда требуется исключение ([thread.req.exception]).
void detach();
Effects: Поток, представленный как, *this продолжает выполнение без блокировки вызывающего потока. Когда detach() возвращается, *this больше не представляет собой возможно продолжающийся поток выполнения. Когда поток, ранее представленный как *this завершает выполнение, реализация должна освободить все принадлежащие ей ресурсы.
Throws: system_error когда требуется исключение ([thread.req.exception]).
id get_id() const noexcept;
unsigned hardware_concurrency() noexcept;
void swap(thread& x, thread& y) noexcept;
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;
template <class Clock, class Duration>
void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
Effects: Блокирует вызывающий поток на время абсолютного тайм-аута ([thread.req.timing]), указанное в abs_time.
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.
Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).
В этом разделе представлены механизмы взаимного исключения: мьютексы, блокировки и однократный вызов. Эти механизмы упрощают создание программ, свободных от расы ([intro.multithread]).
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); }
Объект мьютекса обеспечивает защиту от гонок данных и обеспечивает безопасную синхронизацию данных между ними execution agents. Агент выполнения owns - мьютекс с момента успешного вызова одной из функций блокировки до вызова разблокировки. Мьютексы могут быть рекурсивными или нерекурсивными и могут предоставлять одновременное владение одному или нескольким агентам выполнения. Поставляются как рекурсивные, так и нерекурсивные мьютексы.
mutex types Стандартные библиотеки типов mutex, recursive_mutex, timed_mutex, recursive_timed_mutex, shared_mutex, и shared_timed_mutex. Они должны соответствовать требованиям, изложенным в этом разделе. В этом описании m обозначает объект типа мьютекса.
Типы мьютексов должны быть DefaultConstructible и Destructible. Если инициализация объекта типа мьютекса не удалась,system_error должно быть выброшено исключение типа . Типы мьютексов нельзя копировать или перемещать.
Условия ошибки для кодов ошибок, если таковые имеются, сообщаемые функциями-членами типов мьютексов, должны быть:
resource_unavailable_try_again - если какой-либо управляемый тип дескриптора недоступен.
operation_not_permitted - если поток не имеет права выполнять операцию.
invalid_argument - если какой-либо собственный тип дескриптора, манипулируемый как часть конструкции мьютекса, неверен.
Реализация должна обеспечивать операции блокировки и разблокировки, как описано ниже. В целях определения наличия гонки данных они ведут себя как атомарные операции ([intro.multithread]). Операции блокировки и разблокировки на одном мьютексе должны выполняться в едином общем порядке. [ Note: Это можно рассматривать как modification order мьютекс. ] [ Создание и уничтожение объекта типа мьютекса не обязательно должно быть потокобезопасным; следует использовать другую синхронизацию, чтобы гарантировать, что объекты мьютекса инициализированы и видны другим потокам. ] — end note Note: — end note
Requires: Если m имеет тип mutex, timed_mutex, shared_mutexили shared_timed_mutex, вызывающий поток не является владельцем мьютекса.
Effects: Блокирует вызывающий поток до тех пор, пока для вызывающего потока не будет получено право владения мьютексом.
Synchronization: Предыдущие unlock() операции на одном и том же объекте являются synchronize with этой операцией.
Throws: system_error когда требуется исключение ([thread.req.exception]).
Requires: Если m имеет тип mutex, timed_mutex, shared_mutexили shared_timed_mutex, вызывающий поток не является владельцем мьютекса.
Effects: Пытается получить право собственности на мьютекс для вызывающего потока без блокировки. Если право собственности не получено, нет никакого эффекта и try_lock() немедленно возвращается. Реализация может не получить блокировку, даже если она не удерживается каким-либо другим потоком. [ Note: Этот ложный сбой обычно встречается редко, но допускает интересные реализации, основанные на простом сравнении и обмене (пункт [atomics]). ] Реализация должна гарантировать, что не будет последовательного возврата в отсутствие конкурирующих захватов мьютексов. — end note try_lock() false
Returns: true если право собственности на мьютекс было получено для вызывающего потока, в противном случае false.
Synchronization: Если try_lock() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция. [ Note: Так lock() как не синхронизируется с неудачным последующим try_lock(), правила видимости достаточно слабы, чтобы о состоянии после отказа было бы мало что известно, даже при отсутствии ложных отказов. ] — end note
Synchronization: Эта операция является synchronizes with последующими операциями блокировки, которые получают право собственности на один и тот же объект.
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
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(). Только когда все уровни владения высвобождены, право владения может быть приобретено другим потоком.
timed mutex types Стандартные библиотеки типов timed_mutex, recursive_timed_mutexи shared_timed_mutex. Они должны соответствовать требованиям, изложенным ниже. В этом описании m обозначает объект типа мьютекса, rel_time обозначает объект создания экземпляра durationи abs_time обозначает объект создания экземпляра time_point.
Выражение 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
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
Synchronization: Если try_lock_until() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция.
Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).
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 объектом.
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(). Только когда все уровни владения были освобождены, владение объектом может быть приобретено другим потоком.
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 { }; }
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);
lock_guard(mutex_type& m, adopt_lock_t);
~lock_guard();
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);
~scoped_lock();
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
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);
Postconditions: pm == addressof(m) и owns == res, где res - значение, возвращаемое вызовом m.try_lock().
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);
Requires: Если mutex_type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом. Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.
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 требованиям.
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);
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
~unique_lock();
void lock();
Throws: Любое выброшенное исключение pm->lock(). system_error когда требуется исключение ([thread.req.exception]).
bool try_lock();
Throws: Любое выброшенное исключение pm->try_lock(). system_error когда требуется исключение ([thread.req.exception]).
template <class Clock, class Duration>
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
Requires: Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.
Postconditions: owns == res, где res - значение, возвращаемое вызовом try_lock_until(abs_time).
Throws: Любое выброшенное исключение pm->try_lock_until(). system_error когда требуется исключение ([thread.req.exception]).
template <class Rep, class Period>
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
Requires: Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.
Throws: Любое выброшенное исключение pm->try_lock_for(). system_error когда требуется исключение ([thread.req.exception]).
void unlock();
Throws: system_error когда требуется исключение ([thread.req.exception]).
void swap(unique_lock& u) noexcept;
mutex_type* release() noexcept;
template <class Mutex>
void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;
bool owns_lock() const noexcept;
explicit operator bool() const noexcept;
mutex_type *mutex() const noexcept;
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 note lock() try_lock() unlock() lock() try_lock()
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;
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 ]
Переменные условия предоставляют примитивы синхронизации, используемые для блокировки потока до тех пор, пока какой-либо другой поток не получит уведомление о том, что какое-то условие выполнено, или пока не будет достигнуто системное время. Класс 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 должно быть выполнено в течение трех атомных частей:
Реализация должна вести себя так , как будто все расстрелы notify_one, notify_allи каждую часть wait, wait_forи wait_until казни исполняются в одном неопределенном общем порядке в соответствии с ним «происходит до» порядка.
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 }; }
void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
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
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();
Throws: system_error когда требуется исключение ([thread.req.exception]).
~condition_variable();
Requires: Не должно быть блокировок резьбы *this. [ Note: То есть все потоки должны быть уведомлены; впоследствии они могут заблокировать блокировку, указанную в ожидании. Это ослабляет обычные правила, которые требовали бы, чтобы все вызовы ожидания выполнялись до уничтожения. Только уведомление для разблокировки ожидания должно произойти до уничтожения. Пользователь должен позаботиться о том, что ни один из потоков не ждать *this когда был запущен деструктор, особенно когда ожидающие потоки вызова функции ожидания в цикле или с использованием перегрузок wait, wait_forили wait_until что взять предикат. ] — end note
void notify_one() noexcept;
Effects: Если какие-либо потоки заблокированы в ожидании *this, разблокирует один из этих потоков.
void notify_all() noexcept;
void wait(unique_lock<mutex>& lock);
Requires: lock.owns_lock() является true и lock.mutex() заблокирован вызывающим потоком, и либо
никакой другой поток не ожидает этого condition_variable объекта или
lock.mutex() возвращает то же значение для каждого из lock аргументов , поставляемых всех одновременно ожидания ( с помощью wait, wait_forили wait_untilнитей).
Remarks: Если функция не удовлетворяет постусловию, terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ] — end note
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нитей).
Remarks: Если функция не удовлетворяет постусловию, terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ] — end note
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
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нитей).
Returns: cv_status::timeout если истек относительный тайм-аут ([thread.req.timing]), указанный в rel_time , в противном случае cv_status::no_timeout.
Remarks: Если функция не удовлетворяет постусловию, terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ] — end note
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
[ 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
[ Note: Возвращенное значение указывает, оценивается ли предикат true независимо от того, был ли установлен тайм-аут. ] — end note
Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]) или любое исключение, созданное pred.
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();
Throws: bad_alloc или system_error когда требуется исключение ([thread.req.exception]).
~condition_variable_any();
Requires: Не должно быть блокировок резьбы *this. [ Note: То есть все потоки должны быть уведомлены; впоследствии они могут заблокировать блокировку, указанную в ожидании. Это ослабляет обычные правила, которые требовали бы, чтобы все вызовы ожидания выполнялись до уничтожения. Только уведомление для разблокировки ожидания должно произойти до уничтожения. Пользователь должен позаботиться о том, что ни один из потоков не ждать *this когда был запущен деструктор, особенно когда ожидающие потоки вызова функции ожидания в цикле или с использованием перегрузок wait, wait_forили wait_until что взять предикат. ] — end note
void notify_one() noexcept;
Effects: Если какие-либо потоки заблокированы в ожидании *this, разблокирует один из этих потоков.
void notify_all() noexcept;
template <class Lock>
void wait(Lock& lock);
[ Note: Если какая-либо из wait функций завершается через исключение, не указывается, Lock удерживается ли она. Можно использовать Lock тип, который позволяет запрашивать это, например unique_lock оболочку. ] — end note
Remarks: Если функция не удовлетворяет постусловию, terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ] — end note
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);
Effects:
Атомарно звонит lock.unlock() и блокируется *this.
При разблокировке вызывает lock.lock() (возможно, блокировку по блокировке) и возвращает.
Функция разблокируется при получении сигнала от вызова notify_one(), вызова notify_all(), истечения абсолютного тайм-аута ([thread.req.timing]), указанного с помощью abs_time, или ложно.
Если функция завершается через исключение, она lock.lock() должна вызываться до выхода из функции.
Remarks: Если функция не удовлетворяет постусловию, terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ] — end note
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);
Returns: cv_status::timeout если истек относительный тайм-аут ([thread.req.timing]), указанный в rel_time , в противном случае cv_status::no_timeout.
Remarks: Если функция не удовлетворяет постусловию, terminate() должна быть вызвана ([except.terminate]). [ Note: Это может произойти, если повторная блокировка мьютекса вызывает исключение. ] — end note
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);
[futures] описывает компоненты, которые программа на C ++ может использовать для получения в одном потоке результата (значения или исключения) от функции, которая выполнялась в том же или другом потоке. [ Note: Эти компоненты не ограничиваются многопоточными программами, но также могут быть полезны в однопоточных программах. ] — end note
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
const error_category& future_category() noexcept;
Объектные default_error_condition и эквивалентные виртуальные функции должны вести себя, как указано для класса error_category. name Виртуальная функция объекта должна возвращать указатель на строку "future".
error_code make_error_code(future_errc e) noexcept;
error_condition make_error_condition(future_errc e) noexcept;
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;
const char* what() const noexcept;
Многие из классов, представленных в этом подпункте, используют некоторое состояние для передачи результатов. Он 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
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 { };
promise();
template <class Allocator>
promise(allocator_arg_t, const Allocator& a);
Effects: создает promise объект и общее состояние. Второй конструктор использует распределитель a для выделения памяти для общего состояния.
promise(promise&& rhs) noexcept;
Effects: создает новый promise объект и передает право собственности на совместно используемое состояние rhs (если таковое имеется) на вновь созданный объект.
~promise();
Effects: Отменяет любое общее состояние ([futures.state]).
promise& operator=(promise&& rhs) noexcept;
Effects: Отменяет любое разделяемое состояние ([futures.state]), а затем как если бы promise(std::move(rhs)).swap(*this).
void swap(promise& other) noexcept;
Postconditions: *this имеет общее состояние (если есть), которое other было до вызова swap. other имеет общее состояние (если есть), которое *this было до вызова swap.
future<R> get_future();
Throws: future_error если *this не имеет общего состояния или если get_future уже был вызван в promise с тем же общим состоянием, что и *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.
void set_exception(exception_ptr p);
Effects: Атомарно сохраняет указатель исключения p в общем состоянии и делает это состояние готовым ([futures.state]).
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.
void set_exception_at_thread_exit(exception_ptr p);
Effects: Сохраняет указатель исключения p в общем состоянии, не делая это состояние немедленно готовым. Планирует, что состояние будет подготовлено к выходу из текущего потока после того, как все объекты продолжительности хранения потока, связанные с текущим потоком, будут уничтожены.
template <class R>
void swap(promise<R>& x, promise<R>& y) noexcept;
Шаблон класса future определяет тип для асинхронных возвращаемых объектов, которые не разделяют свое общее состояние с другими асинхронными возвращаемыми объектами. Созданный по умолчанию future объект не имеет общего состояния. future Объект с общим состоянием может быть создан с помощью функций на asynchronous providers или с помощью конструктора перемещения и разделяет его общее состояние с первоначальным асинхронным поставщиком. Результат (значение или исключение) future объекта может быть установлен путем вызова соответствующей функции для объекта, который имеет то же общее состояние.
[ Note: Функции-члены future не синхронизируются сами с собой или с функциями-членами shared_future. ] — end note
Эффект вызова какой - либо функции - члена, кроме деструктора, оператор присваивания-ход, shareили valid на future объекте , для которого valid() == false не определено. [ Note: Действительно перейти от будущего объекта, для которого valid() == false. ] [ Реализациям рекомендуется обнаруживать этот случай и генерировать объект типа с условием ошибки . ] — end note Note: 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;
future(future&& rhs) noexcept;
Effects: Move создает future объект, который ссылается на общее состояние, на которое первоначально ссылался rhs (если таковой имеется).
~future();
future& operator=(future&& rhs) noexcept;
Effects:
Освобождает любое общее состояние ([futures.state]).
переместить Назначает содержимое rhs в *this.
shared_future<R> share() noexcept;
R future::get();
R& future<R&>::get();
void future<void>::get();
[ Note: Как описано выше, шаблон и две его требуемые специализации отличаются только типом возвращаемого значения и возвращаемым значением функции-члена get. ] — end note
Effects:
wait()s, пока общее состояние не будет готово, затем извлекает значение, хранящееся в общем состоянии;
освобождает любое разделяемое состояние ([futures.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;
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]).
Шаблон функции 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 если память для внутренних структур данных не может быть выделена.
[ 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 note — end example
Шаблон класса 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>; }
packaged_task() noexcept;
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.
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;
Postconditions: *this имеет такое же общее состояние и сохраненную задачу (если есть), что и other до вызова swap. other имеет такое же общее состояние и сохраненную задачу (если есть), что и *this до вызова swap.
bool valid() const noexcept;
future<R> get_future();
void operator()(ArgTypes... args);
Effects: Как будто по INVOKE<R>(f, t1, t2, ..., tN), где f хранится задача *this и t1, t2, ..., tN значения в args.... Если задача возвращается нормально, возвращаемое значение сохраняется как асинхронный результат в общем состоянии *this, в противном случае сохраняется исключение, созданное задачей. Общее состояние *this готово, и все потоки, заблокированные в функции, ожидающие *this готовности общего состояния , разблокируются.
Throws: future_error объект исключения , если нет общегосударственных или сохраненное задание уже вызван.
void make_ready_at_thread_exit(ArgTypes... args);
Effects: Как будто по INVOKE<R>(f, t1, t2, ..., tN), где f хранится задача, а t1, t2, ..., tN какие - значения в args.... Если задача возвращается нормально, возвращаемое значение сохраняется как асинхронный результат в общем состоянии *this, в противном случае сохраняется исключение, созданное задачей. В любом случае это должно быть сделано без[futures.state]немедленного преобразования этого состояния в состояние ready ( ). Планирует подготовить общее состояние к моменту выхода из текущего потока после того, как все объекты продолжительности хранения потока, связанные с текущим потоком, будут уничтожены.
void reset();
Effects: Как будто *this = packaged_task(std::move(f)), где f хранится задача *this. [ Note: Это создает новое общее состояние для *this. Старое состояние заброшено ([futures.state]). ] — end note
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>
: true_type { };