Параллельное программирование на С++ в действии. Практика разработки многопоточных программ | страница 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 get_lock() {

> extern std::mutex some_mutex;

> std::unique_lock lk(some_mutex);

> prepare_data();

> return lk; ←(1)

>}


>void process_data() {

> std::unique_lock lk(get_lock()); ←(2)

> do_something();

>}

Поскольку >lk — автоматическая переменная, объявленная внутри функции, то ее можно возвращать непосредственно (1), не вызывая >std:move(); компилятор сам позаботится о вызове перемещающего конструктора. Затем функция >process_data() может передать владение своему экземпляру >std::unique_lock(2), и >do_something() может быть уверена, что подготовленные данные не были изменены каким-то другим потоком.