Объект мьютекса обеспечивает защиту от гонок данных и обеспечивает безопасную синхронизацию данных между ними 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(). Только когда все уровни владения были освобождены, владение объектом может быть приобретено другим потоком.