Параллельное программирование на С++ в действии — страница 50 из 53

В заголовке

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

Содержимое заголовка

namespace std {

class mutex;

class recursive_mutex;

class timed_mutex;

class recursive_timed_mutex;


struct adopt_lock_t;

struct defer_lock_t;

struct try_to_lock_t;


constexpr adopt_lock_t adopt_lock{};

constexpr defer_lock_t defer_lock{};

constexpr try_to_lock_t try_to_lock{};


template

class lock_guard;


template

class unique_lock;


template

void lock(LockableType1& m1, LockableType2& m2...);


template

int try_lock(LockableType1& m1, LockableType2& m2...);


struct once_flag;


template

void call_once(once_flag& flag, Callable func, Args args...);

}

D.5.1. Класс
std::mutex

Класс

std::mutex
предоставляет базовые средства взаимного исключения и синхронизации потоков, применяемые для защиты разделяемых данных. Перед тем как обращаться к данным, защищаемым мьютексом, этот мьютекс необходимо захватить (или заблокировать), вызвав функцию
lock()
или
try_lock()
. В любой момент времени удерживать мьютекс может только один поток; если другой поток попытается захватить тот же мьютекс, то функция
try_lock()
вернет ошибку, а функция
lock()
приостановит выполнение потока. Закончив операции над разделяемыми данными, поток должен вызвать функцию
unlock()
, чтобы освободить мьютекс и дать другим потокам возможность захватить его.

Экземпляр

std::mutex
удовлетворяет требованиям концепции
Lockable
.

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

class mutex {

public:

 mutex(mutex const&)=delete;

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


 constexpr mutex() noexcept;

 ~mutex();


 void lock();

 void unlock();

 bool try_lock();

};

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

Конструирует объект

std::mutex
.

Объявление

constexpr mutex() noexcept;

Результат

Конструирует экземпляр

std::mutex
.

Постусловия

Вновь сконструированный объект

std::mutex
первоначально не захвачен.

Исключения

Нет.

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

Уничтожает объект

std::mutex
.

Объявление

~mutex();

Предусловия

Объект

*this
не должен быть захвачен.

Результат

Уничтожает

*this
.

Исключения

Нет.

STD::MUTEX::LOCK
, ФУНКЦИЯ-ЧЛЕН

Захватывает объект

std::mutex
для текущего потока.

Объявление

void lock();

Предусловия

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

*this
.

Результат

Блокирует текущий поток, пока мьютекс

*this
не будет захвачен.

Постусловия

*this
захвачен текущим потоком.

Исключения

Исключение типа

std::system_error
в случае ошибки.

STD::MUTEX::TRY_LOCK
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить объект

std::mutex
для текущего потока.

Объявление

bool try_lock();

Предусловия

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

*this
.

Результат

Пытается захватить объект

std::mutex
для текущего потока без блокирования.

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

true
, если вызывающий поток захватил мьютекс, иначе
false
.

Постусловия

*this
захвачен вызывающим потоком, если функция вернула
true
.

Исключения

Нет.

Примечание. Функция может не захватить мьютекс (и вернуть

false
), даже если никакой другой поток не удерживает
*this
.

STD::MUTEX::UNLOCK
, ФУНКЦИЯ-ЧЛЕН

Освобождает объект

std::mutex
, удерживаемый текущим потоком.

Объявление

void unlock();

Предусловия

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

*this
.

Результат

Освобождает мьютекс

std::mutex
, удерживаемый текущим потоком.

Если другие потоки были блокированы в ожидании

*this
, то один из них разблокируется.

Постусловия

*this
не захвачен вызывающим потоком.

Исключения

Нет.

D.5.2. Класс
std::recursive_mutex

Класс

std::recursive_mutex
предоставляет базовые средства взаимного исключения и синхронизации потоков, применяемые для защиты разделяемых данных. Перед тем как обращаться к данным, защищаемым мьютексом, этот мьютекс необходимо захватить (или заблокировать), вызвав функцию
lock()
или
try_lock()
. В любой момент времени удерживать мьютекс может только один поток; если другой поток попытается захватить тот же мьютекс, то функция
try_lock()
вернет ошибку, а функция
lock()
приостановит выполнение потока. Закончив операции над разделяемыми данными, поток должен вызвать функцию
unlock()
, чтобы освободить мьютекс и дать другим потокам возможность захватить его.

Этот мьютекс называется рекурсивным, потому что поток, удерживающий мьютекс типа

std::recursive_mutex
, может снова обратиться к функции
lock()
или
try_lock()
, что приведёт к увеличению счетчика захватов. Никакой другой поток не сможет захватить этот мьютекс, пока владеющий им поток не вызовет функцию
unlock
столько раз, сколько было успешных вызовов
lock()
или
try_lock()
.

Экземпляр

std::recursive_mutex
удовлетворяет требованиям концепции
Lockable
.

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

class recursive_mutex {

public:

 recursive_mutex(recursive_mutex const&) = delete;

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

 recursive_mutex() noexcept;

 ~recursive_mutex();


 void lock();

 void unlock();

 bool try_lock() noexcept;

};

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

Конструирует объект

std::recursive_mutex
.

Объявление

recursive_mutex() noexcept;

Результат

Конструирует экземпляр

std::recursive_mutex
.

Постусловия

Вновь сконструированный объект

std::recursive_mutex
первоначально не захвачен.

Исключения

Исключение типа

std::system_error
, если не удалось создать экземпляр
std::recursive_mutex
.

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

Уничтожает объект

std::recursive_mutex
.

Объявление

~recursive_mutex();

Предусловия

Объект

*this
не должен быть захвачен.

Результат

Уничтожает

*this
.

Исключения

Нет.

STD::RECURSIVE_MUTEX::LOCK
, ФУНКЦИЯ-ЧЛЕН

Захватывает объект

std::recursive_mutex
для текущего потока.

Объявление

void lock();

Результат

Блокирует текущий поток, пока мьютекс

*this
не будет захвачен.

Постусловия

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

Исключения

Исключение типа

std::system_error
в случае ошибки.

STD::RECURSIVE_MUTEX::TRY_LOCK
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить объект

std::recursive_mutex
для текущего потока.

Объявление

bool try_lock() noexcept;

Результат

Пытается захватить объект

std::recursive_mutex
для текущего потока без блокирования.

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

true
, если вызывающий поток захватил мьютекс, иначе
false
.

Постусловия

*this
захвачен вызывающим потоком, если функция вернула
true
.

Исключения

Нет.

Примечание. Если вызывающий поток уже удерживал

*this
, то функция возвращает
true
, и счетчик захватов
*this
текущим потоком увеличивается на единицу. Если текущий поток не удерживал
*this
, то функция может не захватить мьютекс (и вернуть
false
), даже если никакой другой поток не удерживает
*this
.

STD::RECURSIVE_MUTEX::UNLOCK
, ФУНКЦИЯ-ЧЛЕН

Освобождает объект

std::recursive_mutex
, удерживаемый текущим потоком.

Объявление

void unlock();

Предусловия

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

*this
.

Результат

Освобождает мьютекс

std::recursive_mutex
, удерживаемый текущим потоком. Если это последний захват
*this
данным потоком, и другие потоки были блокированы в ожидании
*this
, то один из них разблокируется.

Постусловия

Количество захватов

*this
вызывающим потоком, уменьшается на единицу.

Исключения

Нет.

D.5.3. Класс
std::timed_mutex

Класс

std::timed_mutex
предоставляет поддержку блокировок с таймаутами сверх базовых средств взаимного исключения и синхронизации, предоставляемых классом
std::mutex
. Перед тем как обращаться к данным, защищаемым мьютексом, этот мьютекс необходимо захватить (или заблокировать), вызвав функцию l
ock()
,
try_lock()
,
try_lock_for()
или
try_lock_until()
. Если мьютекс уже захвачен другим потоком, то функция
try_lock()
вернет ошибку, функция
lock()
приостановит выполнение потока до получения мьютекса, а функции
try_lock_for()
и
try_lock_until()
приостановят выполнение потока до получения мьютекса или истечения таймаута. Закончив операции над разделяемыми данными, поток должен вызвать функцию
unlock()
(вне зависимости от того, какой функцией мьютекс был захвачен), чтобы освободить мьютекс и дать другим потокам возможность захватить его.

Экземпляр

std::timed_mutex
удовлетворяет требованиям концепции
TimedLockable
.

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

class timed_mutex {

public:

 timed_mutex(timed_mutex const&)=delete;

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

 timed_mutex();

 ~timed_mutex();


 void lock();

 void unlock();

 bool try_lock();


 template

 bool try_lock_for(

  std::chrono::duration const& relative_time);


 template

 bool try_lock_until(

  std::chrono::time_point const& absolute_time);

};

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

Конструирует объект

std::timed_mutex
.

Объявление

timed_mutex();

Результат

Конструирует экземпляр

std::timed_mutex
.

Постусловия

Вновь сконструированный объект

std::timed_mutex
первоначально не захвачен.

Исключения

Исключение типа

std::system_error
, если не удалось создать экземпляр
std::timed_mutex
.

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

Уничтожает объект

std::timed_mutex
.

Объявление

~timed_mutex();

Предусловия

Объект

*this
не должен быть захвачен.

Результат

Уничтожает

*this
.

Исключения

Нет.

STD::TIMED_MUTEX::LOCK
, ФУНКЦИЯ-ЧЛЕН

Захватывает объект

std::timed_mutex
для текущего потока.

Объявление

void lock();

Предусловия

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

*this

Результат

Блокирует текущий поток, пока мьютекс

*this
не будет захвачен.

Постусловия

*this
захвачен текущим потоком.

Исключения

Исключение типа

std::system_error
в случае ошибки.

STD::TIMED_MUTEX::TRY_LOCK
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить объект

std::timed_mutex
для текущего потока.

Объявление

bool try_lock();

Предусловия

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

*this
.

Результат

Пытается захватить объект

std::timed_mutex
для текущего потока без блокирования.

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

true
, если вызывающий поток захватил мьютекс, иначе
false
.

Постусловия

*this
захвачен вызывающим потоком, если функция вернула
true
.

Исключения

Нет.

Примечание. Функция может не захватить мьютекс (и вернуть

false
), даже если никакой другой поток не удерживает
*this
.

STD::TIMED_MUTEX::TRY_LOCK_FOR
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить объект

std::timed_mutex
для текущего потока.

Объявление

template

bool try_lock_for(

 std::chrono::duration const& relative_time);

Предусловия

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

*this
.

Результат

Пытается захватить объект

std::timed_mutex
для текущего потока в течение времени, заданного аргументом
relative_time
. Если
relative_time.count()
равно нулю или отрицательно, то функция возвращается немедленно, как если бы это был вызов
try_lock()
. В противном случае вызывающий поток приостанавливается до получения мьютекса или до истечения времени, заданного аргументом
relative_time
.

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

true
, если вызывающий поток захватил мьютекс, иначе
false
.

Постусловия

*this
захвачен вызывающим потоком, если функция вернула
true
.

Исключения

Нет.

Примечание. Функция может не захватить мьютекс (и вернуть

false
), даже если никакой другой поток не удерживает
*this
. Поток может быть блокирован дольше, чем указано. Если возможно, истекшее время измеряется по стабильным часам.

STD::TIMED_MUTEX::TRY_LOCK_UNTIL
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить объект

std::timed_mutex
для текущего потока.

Объявление

template

bool try_lock_until(

 std::chrono::time_point const& absolute_time);

Предусловия

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

*this
.

Результат

Пытается захватить объект

std::timed_mutex
для текущего потока, пока не наступит момент времени, заданный аргументом
absolute_time
. Если в момент вызова
absolute_time <= Clock::now()
, то функция возвращается немедленно, как если бы это был вызов
try_lock()
. В противном случае вызывающий поток приостанавливается до получения мьютекса или до наступления момента времени, большего или равного
absolute_time
.

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

true
, если вызывающий поток захватил мьютекс, иначе
false
.

Постусловия

*this
захвачен вызывающим потоком, если функция вернула
true
.

Исключения

Нет.

Примечание. Функция может не захватить мьютекс (и вернуть

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

STD::TIMED_MUTEX::UNLOCK
, ФУНКЦИЯ-ЧЛЕН

Освобождает объект

std::timed_mutex
, удерживаемый текущим потоком.

Объявление

void unlock();

Предусловия

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

*this
.

Результат

Освобождает мьютекс

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

Постусловия

*this
не захвачен вызывающим потоком.

Исключения

Нет.

D.5.4. Класс
std::recursive_timed_mutex

Класс

std::recursive_timed_mutex
предоставляет поддержку блокировок с таймаутами сверх базовых средств взаимного исключения и синхронизации, предоставляемых классом
std::recursive_mutex
. Перед тем как обращаться к данным, защищаемым мьютексом, этот мьютекс необходимо захватить (или заблокировать), вызвав функцию
lock()
,
try_lock()
,
try_lock_for()
или
try_lock_until()
. Если мьютекс уже захвачен другим потоком, то функция
try_lock()
вернет ошибку, функция
lock()
приостановит выполнение потока до получения мьютекса, а функции
try_lock_for()
и
try_lock_until()
приостановят выполнение потока до получения мьютекса или истечения таймаута. Закончив операции над разделяемыми данными, поток должен вызвать функцию
unlock()
(вне зависимости от того, какой функцией мьютекс был захвачен), чтобы освободить мьютекс и дать другим потокам возможность захватить его.

Этот мьютекс называется рекурсивным, потому что поток, удерживающий мьютекс типа

std::recursive_timed_mutex
, может снова захватить его любой функцией захвата. Никакой другой поток не сможет захватить этот мьютекс, пока владеющий им поток не вызовет функцию
unlock
столько раз, сколько было успешных вызовов функций захвата.

Экземпляр

std::recursive_timed_mutex
удовлетворяет требованиям концепции
TimedLockable
.

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

class recursive_timed_mutex {

public:

 recursive_timed_mutex(recursive_timed_mutex const&)=delete;

 recursive_timed_mutex& operator=(

  recursive_timed_mutex const&)=delete;


 recursive_timed_mutex();

 ~recursive_timed_mutex();


 void lock();

 void unlock();

 bool try_lock() noexcept;


 template

 bool try_lock_for(

  std::chrono::duration const& relative_time);


 template

 bool try_lock_until(

  std::chrono::time_point const& absolute_time);

};

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

Конструирует объект

std::recursive_timed_mutex
.

Объявление

recursive_timed_mutex();

Результат

Конструирует экземпляр

std::recursive_timed_mutex
.

Постусловия

Вновь сконструированный объект

std::recursive_timed_mutex
первоначально не захвачен.

Исключения

Исключение типа

std::system_error
, если не удалось создать экземпляр
std::recursive_timed_mutex
.

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

Уничтожает объект

std::recursive_timed_mutex
.

Объявление

~recursive_timed_mutex();

Предусловия

Объект

*this
не должен быть захвачен.

Результат

Уничтожает

*this
.

Исключения

Нет.

STD::RECURSIVE_TIMED_MUTEX::LOCK
, ФУНКЦИЯ-ЧЛЕН

Захватывает объект

std::recursive_timed_mutex
для текущего потока.

Объявление

void lock();

Результат

Блокирует текущий поток, пока мьютекс

*this
не будет захвачен.

Постусловия

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

Исключения

Исключение типа

std::system_error
в случае ошибки.

STD::RECURSIVE_TIMED_MUTEX::TRY_LOCK
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить объект

std::recursive_timed_mutex
для текущего потока.

Объявление

bool try_lock() noexcept;

Результат

Пытается захватить объект

std::recursive_timed_mutex
для текущего потока без блокирования.

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

true
, если вызывающий поток захватил мьютекс, иначе
false
.

Постусловия

*this
захвачен вызывающим потоком, если функция вернула
true
.

Исключения

Нет.

Примечание. Если вызывающий поток уже удерживал

*this
, то функция возвращает
true
, и счетчик захватов
*this
текущим потоком увеличивается на единицу. Если текущий поток не удерживал
*this
, то функция может не захватить мьютекс (и вернуть
false
), даже если никакой другой поток не удерживает
*this
.

STD::RECURSIVE_TIMED_MUTEX::TRY_LOCK_FOR
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить объект

std::recursive_timed_mutex
для текущего потока.

Объявление

template

bool try_lock_for(

 std::chrono::duration const& relative_time);

Результат

Пытается захватить объект

std::recursive_timed_mutex
для текущего потока в течение времени, заданного аргументом
relative_time
. Если
relative_time.count()
равно нулю или отрицательно, то функция возвращается немедленно, как если бы это был вызов
try_lock()
. В противном случае вызывающий поток приостанавливается до получения мьютекса или до истечения времени, заданного аргументом
relative_time
.

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

true
, если вызывающий поток захватил мьютекс, иначе
false
.

Постусловия

*this
захвачен вызывающим потоком, если функция вернула
true
.

Исключения

Нет.

Примечание. Если вызывающий поток уже удерживал

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

STD::RECURSIVE_TIMED_MUTEX::TRY_LOCK_UNTIL
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить объект

std::recursive_timed_mutex
для текущего потока.

Объявление

template

bool try_lock_until(

 std::chrono::time_point const& absolute_time);

Результат

Пытается захватить объект

std::recursive_timed_mutex
для текущего потока, пока не наступит момент времени, заданный аргументом
absolute_time
. Если в момент вызова
absolute_time <= Clock::now()
, то функция возвращается немедленно, как если бы это был вызов
try_lock()
. В противном случае вызывающий поток приостанавливается до получения мьютекса или до наступления момента времени, большего или равного
absolute_time
.

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

true
, если вызывающий поток захватил мьютекс, иначе
false
.

Постусловия

*this
захвачен вызывающим потоком, если функция вернула
true
.

Исключения

Нет.

Примечание. Если вызывающий поток уже удерживал

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

STD::RECURSIVE_TIMED_MUTEX::UNLOCK
, ФУНКЦИЯ-ЧЛЕН

Освобождает объект

std::recursive_timed_mutex
, удерживаемый текущим потоком.

Объявление

void unlock();

Предусловия

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

*this
.

Результат

Освобождает мьютекс

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

Постусловия

Количество захватов

*this
вызывающим потоком, уменьшается на единицу.

Исключения

Нет.

D.5.5. Шаблон класса
std::lock_guard

Шаблон класса

std::lock_guard
предоставляет простую обертку владения блокировкой. Тип блокируемого мьютекса задается параметром шаблона
Mutex
и должен удовлетворять требованиям концепции
Lockable
. Заданный мьютекс захватывается в конструкторе и освобождается в деструкторе. Тем самым мы получаем простое средство захвата мьютекса в некотором блоке кода, которое гарантирует освобождение мьютекса при выходе из блока вне зависимости от того, как этот выход произведен: по достижении закрывающей скобки, в результате предложения, меняющего поток управления, например
break
или
return
, или вследствие исключения.

Экземпляры

std::lock_guard
не удовлетворяют требованиям концепций
MoveConstructible
,
CopyConstructible
и
CopyAssignable
.

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

template 

class lock_guard {

public:

 typedef Mutex mutex_type;


 explicit lock_guard(mutex_type& m);

 lock_guard(mutex_type& m, adopt_lock_t);

 ~lock_guard();


 lock_guard(lock_guard const&) = delete;

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

};

STD::LOCK_GUARD
, ЗАХВАТЫВАЮЩИЙ КОНСТРУКТОР

Конструирует экземпляр

std::lock_guard
, который захватывает указанный мьютекс.

Объявление

explicit lock_guard(mutex_type& m);

Результат

Конструирует экземпляр

std::lock_guard
, который ссылается на указанный мьютекс. Вызывает
m.lock()
.

Исключения

Любое исключение, возбуждаемое

m.lock()
.

Постусловия

*this
владеет блокировкой
m
.

STD::LOCK_GUARD
, КОНСТРУКТОР, ПЕРЕНИМАЮЩИЙ БЛОКИРОВКУ

Конструирует экземпляр

std::lock_guard
, который владеет блокировкой указанного мьютекса.

Объявление

lock_guard(mutex_type& m, std::adopt_lock_t);

Предусловия

Вызывающий поток должен владеть блокировкой

m
.

Результат

Конструирует экземпляр

std::lock_guard
, который ссылается на указанный мьютекс и принимает владение блокировкой
m
, удерживаемой вызывающим потоком.

Исключения

Нет.

Постусловия

*this
владеет блокировкой
m
, удерживаемой вызывающим потоком.

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

Уничтожает экземпляр

std::lock_guard
и освобождает соответствующий мьютекс.

Объявление

~lock_guard();

Результат

Вызывает

m.unlock()
для мьютекса
m
, заданного при конструировании
*this
.

Исключения

Нет.

D.5.6. Шаблон класса
std::unique_lock

Шаблон класса

std::unique_lock
предоставляет более общую обертку владения блокировкой, чем
std::lock_guard
. Тип блокируемого мьютекса задается параметром шаблона
Mutex
и должен удовлетворять требованиям концепции
BasicLockable
. Вообще говоря, заданный мьютекс захватывается в конструкторе и освобождается в деструкторе, хотя имеются также дополнительные конструкторы и функции-члены, предлагающие другие возможности. Тем самым мы получаем средство захвата мьютекса в некотором блоке кода, которое гарантирует освобождение мьютекса при выходе из блока вне зависимости от того, как этот выход произведен: по достижении закрывающей скобки, в результате предложения, меняющего поток управления, например
break
или
return
, или вследствие исключения.

Функции ожидания в классе

std::condition_variable
требуют объекта
std::unique_lock
, и любая конкретизация шаблона
std::unique_lock
может быть использована в качестве параметра типа
Lockable
в любом варианте функции
wait
из класса
std::condition_variable_any
.

Если тип

Mutex
удовлетворяет требованиям концепции
Lockable
, то им удовлетворяет и тип
std::unique_lock
. Если, кроме того, тип
Mutex
удовлетворяет требованиям концепции
TimedLockable
, то им удовлетворяет и тип
std::unique_lock
.

Экземпляры

std::unique_lock
удовлетворяют требованиям концепций
MoveConstructible
и
MoveAssignable
, но не
CopyConstructible
и
CopyAssignable
.

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

template 

class unique_lock {

public:

 typedef Mutex mutex_type;


 unique_lock() noexcept;

 explicit unique_lock(mutex_type& m);

 unique_lock(mutex_type& m, adopt_lock_t);

 unique_lock(mutex_type& m, defer_lock_t) noexcept;

 unique_lock(mutex_type& m, try_to_lock_t);


 template

 unique_lock(

  mutex_type& m,

  std::chrono::time_point const& absolute_time);


 template

 unique_lock(

  mutex_type& m,

  std::chrono::duration const& relative_time);


 ~unique_lock();

 unique_lock(unique_lock const&) = delete;

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


 unique_lock(unique_lock&&);

 unique_lock& operator=(unique_lock&&);


 void swap(unique_lock& other) noexcept;


 void lock();

 bool try_lock();

 template

 bool try_lock_for(

  std::chrono::duration const& relative_time);

 template

 bool try_lock_until(

  std::chrono::time_point const& absolute_time);

 void unlock();


 explicit operator bool() const noexcept;

 bool owns_lock() const noexcept;

 Mutex* mutex() const noexcept;

 Mutex* release() noexcept;

};

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

Конструирует экземпляр

std::unique_lock
, с которым не ассоциирован мьютекс.

Объявление

unique_lock() noexcept;

Результат

Конструирует экземпляр

std::unique_lock
, с которым не ассоциирован мьютекс.

Постусловия

this->mutex() == NULL
,
this->owns_lock() == false
.

STD::UNIQUE_LOCK
, ЗАХВАТЫВАЮЩИЙ КОНСТРУКТОР

Конструирует экземпляр

std::unique_lock
, который захватывает указанный мьютекс.

Объявление

explicit unique_lock(mutex_type& m);

Результат

Конструирует экземпляр

std::unique_lock
, который ссылается на указанный мьютекс.
Вызывает m.lock()
.

Исключения

Любое исключение, возбуждаемое

m.lock()
.

Постусловия

this->owns_lock() == true
,
this->mutex() == &m
.

STD::UNIQUE_LOCK
, КОНСТРУКТОР, ПЕРЕНИМАЮЩИЙ БЛОКИРОВКУ

Конструирует экземпляр

std::unique_lock
, который владеет блокировкой указанного мьютекса.

Объявление

unique_lock(mutex_type& m, std::adopt_lock_t);

Предусловия

Вызывающий поток должен владеть блокировкой

m
.

Результат

Конструирует экземпляр

std::unique_lock
, который ссылается на указанный мьютекс и принимает владение блокировкой
m
, удерживаемой вызывающим потоком.

Исключения

Нет.

Постусловия

this->owns_lock() == true
,
this->mutex() == &m
.

STD::UNIQUE_LOCK
, КОНСТРУКТОР ОТЛОЖЕННОЙ БЛОКИРОВКИ

Конструирует экземпляр

std::unique_lock
, который не владеет блокировкой указанного мьютекса.

Объявление

unique_lock(mutex_type& m, std::defer_lock_t) noexcept;

Результат

Конструирует экземпляр

std::unique_lock
, который ссылается на указанный мьютекс.

Исключения

Нет.

Постусловия

this->owns_lock() == false
,
this->mutex() == &m
.

STD::UNIQUE_LOCK
, КОНСТРУКТОР ПРОБНОЙ БЛОКИРОВКИ

Конструирует экземпляр

std::unique_lock
, ассоциированный с указанным мьютексом, и пытается захватить этот мьютекс.

Объявление

unique_lock(mutex_type& m, std::try_to_lock_t);

Предусловия

Тип

Mutex
, которым конкретизирован шаблон
std::unique_lock
, должен удовлетворять требованиям концепции
Lockable
.

Результат

Конструирует экземпляр

std::unique_lock
, который ссылается на указанный мьютекс. Вызывает
m.try_lock()
.

Исключения

Нет.

Постусловия

this->owns_lock()
возвращает результат вызова
m.try_lock()
,
this->mutex() == &m
.

STD::UNIQUE_LOCK
, КОНСТРУКТОР ПРОБНОЙ БЛОКИРОВКИ С ОТНОСИТЕЛЬНЫМ ТАЙМАУТОМ

Конструирует экземпляр

std::unique_lock
, ассоциированный с указанным мьютексом, и пытается захватить этот мьютекс.

Объявление

template

unique_lock(

 mutex_type& m,

 std::chrono::duration const& relative_time);

Предусловия

Тип

Mutex
, которым конкретизирован шаблон
std::unique_lock
, должен удовлетворять требованиям концепции
TimedLockable
.

Результат

Конструирует экземпляр

std::unique_lock
, который ссылается на указанный мьютекс. Вызывает
m.try_lock_for(relative_time)
.

Исключения

Нет.

Постусловия

this->owns_lock()
возвращает результат вызова
m.try_lock_for()
,
this->mutex() == &m
.

STD::UNIQUE_LOCK
, КОНСТРУКТОР ПРОБНОЙ БЛОКИРОВКИ С АБСОЛЮТНЫМ ТАЙМАУТОМ

Конструирует экземпляр

std::unique_lock
, ассоциированный с указанным мьютексом, и пытается захватить этот мьютекс.

Объявление

template

unique_lock(

 mutex_type& m,

 std::chrono::time_point const& absolute_time);

Предусловия

Тип

Mutex
, которым конкретизирован шаблон
std::unique_lock
, должен удовлетворять требованиям концепции
TimedLockable
.

Результат

Конструирует экземпляр

std::unique_lock
, который ссылается на указанный мьютекс. Вызывает
m.try_lock_until(relative_time)
.

Исключения

Нет.

Постусловия

this->owns_lock()
возвращает результат вызова
m.try_lock_until()
,
this->mutex() == &m
.

STD::UNIQUE_LOCK
, ПЕРЕМЕЩАЮЩИЙ КОНСТРУКТОР

Передает владение блокировкой от существующего объекта

std::unique_lock
вновь сконструированному.

Объявление

unique_lock(unique_lock&& other) noexcept;

Результат

Конструирует экземпляр

std::unique_lock
. Если объект
other
владел блокировкой мьютекса до вызова конструктора, то теперь этой блокировкой владеет вновь сконструированный объект
std::unique_lock
.

Постусловия

Для вновь сконструированного объекта

std::unique_lock x
,
x.mutex()
равно значению
other.mutex()
до вызова конструктора, а
x.owns_lock()
равно значению
other.owns_lock()
до вызова конструктора.
other.mutex() == NULL
,
other.owns_lock() == false
.

Исключения

Нет.

Примечание. Объекты

std::unique_lock
не удовлетворяют требованиям концепции
CopyConstructible
, поэтому копирующего конструктора не существует, существует только этот перемещающий конструктор.

STD::UNIQUE_LOCK
, ПЕРЕМЕЩАЮЩИЙ ОПЕРАТОР ПРИСВАИВАНИЯ

Передает владение блокировкой от одного объекта

std: :unique_ lock
другому.

Объявление

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

Результат

Если

this->owns_lock()
возвращала
true
до вызова этого оператора, то вызывает
this->unlock()
. Если объект
other
владел блокировкой мьютекса до присваивания, то теперь этой блокировкой владеет
*this
.

Постусловия

this.mutex()
равно значению
other.mutex()
до присваивания, а
this.owns_lock()
равно значению
other.owns_lock()
до присваивания.
other.mutex() == NULL
,
other.owns_lock() == false
.

Исключения

Нет.

Примечание. Объекты

std::unique_lock
не удовлетворяют требованиям концепции
CopyAssignable
, поэтому копирующего оператора присваивания не существует, существует только этот перемещающий оператор присваивания.

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

Уничтожает экземпляр

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

Объявление

~unique_lock();

Результат

Если

this->owns_lock()
возвращает
true
, то вызывает
this->mutex()->unlock()
.

Исключения

Нет.

STD::UNIQUE_LOCK::SWAP
, ФУНКЦИЯ-ЧЛЕН

Обменивает владение ассоциированными блокировками мьютекса между двумя объектами

std::unique_lock
.

Объявление

void swap(unique_lock& other) noexcept;

Результат

Если

other
владел блокировкой мьютекса до вызова, то теперь этой блокировкой владеет
*this
. Если
*this
владел блокировкой мьютекса до вызова, то теперь этой блокировкой владеет
other
.

Постусловия

this.mutex()
равно значению
other.mutex()
до вызова,
other.mutex()
равно значению
this.mutex()
до вызова,
this.owns_lock()
равно значению
other.owns_lock()
до вызова,
other.owns_lock()
равно значению
this.owns_lock()
до вызова.

Исключения

Нет.

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

Обменивает владение ассоциированными блокировками мьютекса между двумя объектами

std::unique_lock
.

Объявление

void swap(unique_lock& lhs, unique_lock& rhs) noexcept;

Результат

lhs.swap(rhs)

Исключения

Нет.

STD::UNIQUE_LOCK::LOCK
, ФУНКЦИЯ-ЧЛЕН

Захватывает мьютекс, ассоциированный с

*this
.

Объявление

void lock();

Предусловия

this->mutex() != NULL
,
this->owns_lock() == false
.

Результат

Вызывает

this->mutex()->lock()
.

Исключения

Любое исключение, возбужденное

this->mutex()->lock()
. Исключение типа
std::system_error
с кодом ошибки
std::errc::operation_not_permitted
, если
this->mutex() == NULL
. Исключение типа
std::system_error
с кодом ошибки
std::errc::resource_deadlock_would_occur
, если
this->owns_lock() == true
в момент вызова.

Постусловия

this->owns_lock() == true
.

STD::UNIQUE_LOCK::TRY_LOCK
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить мьютекс, ассоциированный с

*this
.

Объявление

bool try_lock();

Предусловия

Тип

Mutex
, которым конкретизируется шаблон
std::unique_lock
, должен удовлетворять требованиям концепции
Lockable
.
this->mutex() != NULL
,
this->owns_lock() == false
.

Результат

Вызывает

this->mutex ()->try_lock()
.

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

true
, если вызов
this->mutex()->try_lock()
вернул
true
, иначе
false
.

Исключения

Любое исключение, возбужденное

this->mutex()->try_lock()
. Исключение типа
std::system_error
с кодом ошибки
std::errc::operation_not_permitted
, если
this->mutex() == NULL
. Исключение типа
std::system_error
с кодом ошибки
std::errc::resource_deadlock_would_occur
, если
this->owns_lock() == true
в момент вызова.

Постусловия

Если функция возвращает

true
, то
this->owns_lock() == true
, иначе
this->owns_lock() == false
.

STD::UNIQUE_LOCK::UNLOCK
, ФУНКЦИЯ-ЧЛЕН

Освобождает мьютекс, ассоциированный с

*this
.

Объявление

void unlock();

Предусловия

this->mutex() != NULL
,
this->owns_lock() == true
.

Результат

Вызывает

this->mutex()->unlock()
.

Исключения

Любое исключение, возбужденное

this->mutex()->unlock()
. Исключение типа
std::system_error
с кодом ошибки
std::errc::operation_not_permitted
, если
this->owns_lock() == false
в момент вызова.

Постусловия

this->owns_lock() == false
.

STD::UNIQUE_LOCK::TRY_LOCK_FOR
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить мьютекс, ассоциированный с

*this
, в течение указанного времени.

Объявление

template

bool try_lock_for(

std::chrono::duration const& relative_time);

Предусловия

Тип

Mutex
, которым конкретизируется шаблон
std::unique_lock
, должен удовлетворять требованиям концепции
TimedLockable
.
this->mutex() != NULL
,
this->owns_lock() == false
.

Результат

Вызывает

this->mutex ()->try_lock_for(relative_time)
.

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

true
, если вызов
this->mutex()->try_lock_for()
вернул
true
, иначе
false
.

Исключения

Любое исключение, возбужденное

this->mutex()->try_lock_for()
. Исключение типа
std::system_error
с кодом ошибки
std::errc::operation_not_permitted
, если
this->mutex() == NULL
. Исключение типа
std::system_error
с кодом ошибки
std::errc::resource_deadlock_would_occur
, если
this->owns_lock() == true
в момент вызова.

Постусловия

Если функция вернула

true
, то
this->owns_lock() == true
, иначе
this->owns_lock() == false
.

STD::UNIQUE_LOCK::TRY_LOCK_UNTIL
, ФУНКЦИЯ-ЧЛЕН

Пытается захватить мьютекс, ассоциированный с

*this
, в течение указанного времени.

Объявление

template

bool try_lock_until(

std::chrono::time_point const& absolute_time);

Предусловия

Тип

Mutex
, которым конкретизируется шаблон
std::unique_lock
, должен удовлетворять требованиям концепции
TimedLockable
.
this->mutex() != NULL
,
this->owns_lock() == false
.

Результат

Вызывает

this->mutex()->try_lock_until(absolute_time)
.

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

true
, если вызов
this->mutex()->try_lock_until()
вернул
true
, иначе
false
.

Исключения

Любое исключение, возбужденное

this->mutex()->try_lock_until()
. Исключение типа
std::system_error
с кодом ошибки
std::errc::operation_not_permitted
, если
this->mutex() == NULL
. Исключение типа
std::system_error
с кодом ошибки
std::errc::resource_deadlock_would_occur
, если
this->owns_lock() == true
в момент вызова.

Постусловия

Если функция вернула

true
, то
this->owns_lock() == true
, иначе
this->owns_lock() == false
.

STD::UNIQUE_LOCK::OPERATOR BOOL
, ФУНКЦИЯ-ЧЛЕН

Проверяет, владеет ли

*this
блокировкой мьютекса.

Объявление

explicit operator bool() const noexcept;

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

this->owns_lock()
. Исключения

Нет.

Примечание. Это оператор явного преобразования, поэтому он вызывается неявно только в контекстах, где результат используется как булевское значение, а не тогда, когда результат трактуется как целое, равное 0 или 1.

STD::UNIQUE_LOCK::OWNS_LOCK
, ФУНКЦИЯ-ЧЛЕН

Проверяет, владеет ли

*this
блокировкой мьютекса.

Объявление

bool owns_lock() const noexcept;

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

true
, если
*this
владеет блокировкой мьютекса, иначе
false
.

Исключения

Нет.

STD::UNIQUE_LOCK::MUTEX
, ФУНКЦИЯ-ЧЛЕН

Возвращает мьютекс, ассоциированный с

*this
, если таковой имеется.

Объявление

mutex_type* mutex() const noexcept;

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

Указатель на мьютекс, ассоциированный с

*this
, если таковой имеется, иначе
NULL
.

Исключения

Нет.

STD::UNIQUE_LOCK::RELEASE
, ФУНКЦИЯ-ЧЛЕН

Возвращает мьютекс, ассоциированный с

*this
, если таковой имеется, и разрывает эту ассоциацию.

Объявление

mutex_type* release() noexcept;

Результат

Разрывает ассоциацию мьютекса с

*this
, не освобождая блокировку.

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

Указатель на мьютекс, ассоциированный с

*this
, если таковой имеется, иначе
NULL
.

Постусловия

this->mutex() == NULL
,
this->owns_lock() == false.

Исключения

Нет.

Примечание. Если

this->owns_lock()
вернула бы до этого обращения
true
, то с этого момента за освобождение мьютекса отвечает вызывающая программа.

D.5.7. Шаблон функции
std::lock

Шаблон функции

std::lock
предоставляет возможность захватить сразу несколько мьютексов, не опасаясь возникновения взаимоблокировки из-за несогласованного порядка захвата.

Объявление

template

void lock(LockableType1& m1, LockableType2& m2...);

Предусловия

Типы параметров

LockableType1
,
LockableType2
, … должны удовлетворять требованиям концепции
Lockable
.

Результат

Захватывает все объекты

m1
,
m2
, … допускающих блокировку типов, обращаясь к функциям-членам
lock()
,
try_lock()
и
unlock()
этих типов в порядке, который гарантированно не приводит к взаимоблокировкам, но в остальном не специфицирован.

Постусловия

Текущий поток захватывает все переданные в аргументах объекты.

Исключения

Любое исключение, возбуждаемое обращениями к функциям

lock()
,
try_lock()
и
unlock()
.

Примечание. Если исключение распространяется за пределы

std::lock
, то для любого объекта
m1
,
m2
, …, для которого в результате обращения к
lock()
или
try_lock()
была успешно получена блокировка, гарантированно будет вызвана функция
unlock()
.

D.5.8. Шаблон функции
std::try_lock

Шаблон функции

std::try_lock
предоставляет возможность захватить сразу несколько допускающих блокировку объектов, так что либо захвачены все, либо ни один.

Объявление

template

int try_lock(LockableType1& m1, LockableType2& m2...);

Предусловия

Типы параметров

LockableType1
,
LockableType2
, … должны удовлетворять требованиям концепции
Lockable
.

Результат

Пытается захватить все объекты

m1
,
m2
, … допускающих блокировку типов, обращаясь по очереди к функции
try_lock()
каждого из них. Если
try_lock()
вернёт
false
или возбудит исключение, то уже захваченные блокировки освобождаются путем вызова функции
unlock()
соответствующего объекта.

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

-1, если были захвачены все блокировки (то есть все вызовы

try_lock()
вернули
true
), в противном случае начинающийся с нуля индекс объекта, для которого вызов
try_lock()
вернул
false
.

Постусловия

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

Исключения

Любое исключение, возбуждаемое обращениями к функции

try_lock
.

Примечание. Если исключение распространяется за пределы

std::try_lock
, то для любого объекта
m1
,
m2
, …, для которого в результате обращения к
try_lock()
была успешно получена блокировка, гарантированно будет вызвана функция
unlock()
.

D.5.9. Класс
std::once_flag

Экземпляры класса

std::once_flag
используются совместно с шаблоном функции
std::call_once
для гарантии того, что некая функция будет вызвала ровно один раз, даже если ее могут вызывать одновременно несколько потоков.

Экземпляры

std::once_flag
не удовлетворяют требованиям концепций
CopyConstructible
,
CopyAssignable
,
MoveConstructible
и
MoveAssignable
.

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

struct once_flag {

 constexpr once_flag() noexcept;


 once_flag(once_flag const&) = delete;

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

};

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

Создает объект

std::once_flag
в состоянии, обозначающем, что ассоциированная функция еще не вызывалась.

Объявление

constexpr once_flag() noexcept;

Результат

Конструирует новый экземпляр

std::once_flag
, оставляя его в состоянии, означающем, что ассоциированная функция еще не вызывалась. Поскольку в конструкторе присутствует квалификатор
constexpr
, то экземпляр со статическим временем жизни конструируется на этапе статической инициализации, что предотвращает состояние гонки и зависимость от порядка инициализации.

D.5.10. Шаблон функции
std::call_once

Шаблон функции

std::call_once
используется совместно с объектом
std::once_flag
для гарантии того, что некая функция будет вызвала ровно один раз, даже если ее могут вызывать одновременно несколько потоков.

Объявление

template

void call_once(

 std::once_flag& flag, Callable func, Args args...);

Предусловия

Выражение

INVOKE(func, args)
допустимо для переданных значений
func
и
args
. Тип
Callable
и все члены
Args
удовлетворяют требованиям концепции
MoveConstructible
.

Результат

Обращения к

std::call_once
с одним и тем же объектом
std::once_flag
сериализуются. Если раньше не было результативного обращения к
std::call_once
с данным объектом
std::once_flag
, то аргумент
func
(или его копия) вызывается так, будто имело место обращение к
INVOKE(func, args)
, причем вызов
std::call_once
считается результативным тогда и только тогда, когда вызов
func
завершился без возбуждения исключения. Если имело место исключение, то оно передается вызывающей программе. Если ранее уже было результативное обращение к
std::call_once
с данным объектом
std::once_flag
, то новый вызов
std::call_once
возвращает управление, не вызывая
func
.

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

Возврат из результативного вызова

std::call_once
с объектом
std::once_flag
происходит-раньше всех последующих вызовов
std::call_once
с тем же объектом
std::once_flag
.

Исключения

Исключение типа

std::system_error
, если желаемого эффекта добиться не удалось, или любое исключение, возбужденное при обращении к
func
.

D.6. Заголовок