Параллельное программирование на С++ в действии. Практика разработки многопоточных программ | страница 61
Естественно, этот флаг необходимо где-то хранить. Поэтому размер объекта >std::unique_lock
обычно больше, чем объекта >std::lock_guard
, и работает >std::unique_lock
чуть медленнее >std::lock_guard
, потому что флаг нужно проверять и обновлять. Если класс >std::lock_guard
отвечает вашим нуждам, то я рекомендую использовать его. Тем не менее, существуют ситуации, когда >std::unique_lock
лучше отвечает поставленной задаче, так как без свойственной ему дополнительной гибкости не обойтись. Один из примеров — показанный выше отложенный захват; другой — необходимость передавать владение мьютексом из одного контекста в другой.
3.2.7. Передача владения мьютексом между контекстами
Поскольку экземпляры >std::unique_lock
не владеют ассоциированными мьютексами, то можно передавать владение от одного объекта другому путем перемещения. В некоторых случаях передача производится автоматически, например при возврате объекта из функции, а иногда это приходится делать явно, вызывая >std::move()
. Ситуация зависит от того, является ли источник l-значением — именованной переменной или ссылкой на нее — или r-значением — временным объектом. Если источник — r-значение, то передача владения происходит автоматически, в случае же l-значение это нужно делать явно, чтобы не получилось так, что переменная потеряет владение непреднамеренно. Класс >std::unique_lock
дает пример перемещаемого, но не копируемого типа. Дополнительные сведения о семантике перемещения см. в разделе А.1.1 приложения А.
Одно из возможных применений — разрешить функции захватить мьютекс, а потом передать владение им вызывающей функции, чтобы та могла выполнить дополнительные действия под защитой того же мьютекса. Ниже приведен соответствующий пример — функция >get_lock()
захватывает мьютекс, подготавливает некоторые данные, а потом возвращает мьютекс вызывающей программе:
>std::unique_lock
> extern std::mutex some_mutex;
> std::unique_lock
> prepare_data();
> return lk; ←
(1)
>}
>void process_data() {
> std::unique_lock
(2)
> do_something();
>}
Поскольку >lk
— автоматическая переменная, объявленная внутри функции, то ее можно возвращать непосредственно (1), не вызывая >std:move()
; компилятор сам позаботится о вызове перемещающего конструктора. Затем функция >process_data()
может передать владение своему экземпляру >std::unique_lock
(2), и >do_something()
может быть уверена, что подготовленные данные не были изменены каким-то другим потоком.