std::unique_lockはミューテックスインタフェースを持つ
std::unique_lockクラスはstd::lock_guardクラスと違って多くの機能を持っています。
ひとつは昨日書いたムーブですが、もうひとつは「ミューテックスインタフェース」を持っているということです。
std::unique_lock
Mutexが、std::timed_mutexやstd::recursive_timed_mutexのように、try_lock_for()/try_lock_until()のインタフェースを持っているクラスであれば、std::unique_lockクラスもそのインタフェースを使用できます。
そのため、std::unique_lockクラス自体を、std::lock_guardクラスやstd::unique_lockクラスのテンプレート引数として指定できます。やってみましょう。
#include <iostream> #include <thread> #include <mutex> class X { int value_ = 0; mutable std::mutex mtx_; public: void add_value() { std::unique_lock<std::mutex> outer_lock(mtx_, std::defer_lock); // すぐにはロックしない std::lock_guard<std::unique_lock<std::mutex>> inner_lock(outer_lock); // ここでロックする ++value_; } // inner_lockのデストラクタでunlock()される // outer_lock(std::unique_lock)はunlock()しない int get() const { std::lock_guard<std::mutex> lk(mtx_); return value_; } }; int main() { X x; std::thread t1([&] { x.add_value(); }); std::thread t2([&] { x.add_value(); }); t1.join(); t2.join(); std::cout << x.get() << std::endl; }
std::unique_lockクラスはアンロック状態であればデストラクタでunlock()を呼ばないので、こういうネストができるのでした。
参照:
std::unique_lock - cpprefjp
std::lock_guard - cpprefjp