Параллельное программирование на С++ в действии. Практика разработки многопоточных программ | страница 60
>std::unique_lock
не обязан владеть ассоциированным с ним мьютексом. Прежде всего, в качестве второго аргумента конструктору можно передавать не только объект >std::adopt_lock
, заставляющий объект управлять захватом мьютекса, но и объект >std::defer_lock
, означающий, что в момент конструирования мьютекс не должен захватываться. Захватить его можно будет позже, вызвав функцию-член >lock()
объекта >std::unique_lock
(а не самого мьютекса) или передав функции >std::lock()
сам объект >std::unique_lock
. Код в листинге 3.6 можно было бы с тем же успехом написать, как показало в листинге 3.9, с применением >std::unique_lock
и >std::defer_lock()
(1) вместо >std::lock_guard
и >std::adopt_lock
. В новом варианте столько же строк, и он эквивалентен исходному во всем, кроме одной детали, — >std::unique_lock
потребляет больше памяти и выполняется чуть дольше, чем >std::lock_guard
. Та гибкость, которую мы получаем, разрешая экземпляру >std::unique_lock
не владеть мьютексом, обходится не бесплатно — дополнительную информацию надо где-то хранить и обновлять.Листинг 3.9. Применение >std::lock()
и >std::unique_guard
для реализации операции обмена
>class some_big_object;
>void swap(some_big_object& lhs,some_big_object& rhs);
>class X {
>private:
> some_big_object some_detail;
> std::mutex m;
>public:
> X(some_big_object const& sd): some_detail(sd) {}
> friend void swap(X& lhs, X& rhs) {
> if (&lhs == &rhs)
std::defer_lock оставляет
> return;
мьютексы не захваченными (1)
> std::unique_lock
> std::unique_lock
> std::lock(lock_a, lock_b); ←
(2) Мьютексы захватываются
> swap(lhs.some_detail, rhs.some_detail);
> }
>};
В листинге 3.9 объекты >std::unique_lock
можно передавать функции >std::lock()
(2), потому что в классе >std::unique_lock
имеются функции-члены >lock()
, >try_lock()
и >unlock()
. Для выполнения реальной работы они вызывают одноименные функции контролируемого мьютекса, а сами только поднимают в экземпляре >std::unique_lock
флаг, показывающий, что в данный момент этот экземпляр владеет мьютексом. Флаг необходим для того, чтобы деструктор знал, вызывать ли функцию >unlock()
. Если экземпляр действительно владеет мьютексом, то деструктор должен вызвать >unlock()
, в противном случае — не должен. Опросить состояние флага позволяет функция-член