この記事は、C++ Advent Calendar 2016の12日目の記事です。
std::lock_guardクラステンプレートは、std::mutexなどのlockとunlockをRAII化するものとして欠かせません。
std::mutex m; std::lock_guard<std::mutex> guard(m); |
しかし、このようにテンプレート実引数としてミューテックスの型を指定しなければならないのが少しです。そこで、Boost.Thraedの中からこの点少し便利になったものを2つ紹介します。どれもヘッダーのみ、ビルド不要で使えます。
make_lock_guard
まずは、lock_guardオブジェクトを作るboost::make_lock_guard関数テンプレートです。std::make_pairなどのように、よくある関数テンプレートです。ただし、ミューテックスやlock guardは要件(コンセプト)でコピー・ムーブ可能となっていないので、このように参照で受ける必要があります。
std::mutex m; auto&& guard = boost::make_lock_guard(m); // 以下も可。 // const auto& guard = boost::make_lock_guard(m); |
なお、boost::make_unique_lockとboost::make_unique_locksもあります。boost::make_unique_locksは複数個のミューテックスを一挙にロックします。内部では、boost::lock関数を呼び出しています。
std::mutex m1 std::recursive_mutex m2; auto locks = boost::make_unique_locks(m1, m2); // locksの型はstd::tuple<std::mutex, std::recursive_mutex> |
なお、boost::adopt_lockを実引数として受け取るboost::make_lock_guardや、boost::adopt_lock, boost::defer_lock, boost::try_to_lockのそれぞれを実引数として受け取るboost::make_unique_lockもあります。
- boost::make_lock_guardを使うには、<boost/thread/lock_guard.hpp>か<boost/thread/locks.hpp>をインクルードします。
- boost::make_unique_lockやboost::make_unique_locksを使うには、<boost/thread/lock_factories.hpp>をインクルードします。
以下、Boostのリファレンスページへのリンクです。
- Non Member Function make_lock_guard
- Non Member Function make_unique_lock(Lockable&)
- Non Member Function make_unique_locks(Lockable& …)
- Non-member function lock(Lockable1,Lockable2,…)
with_lock_guard
次は、boost::with_lock_guard関数テンプレートです。これは、ミューテックスをロックしてから、指定の関数オブジェクトを呼び出すというものです。もちろん、関数オブジェクトから脱出したら、ロック解除されます。
std::mutex m; int x = boost::with_lock_guard(m, [&]{ // ロックされている状態。 // いろんな処理を実行する。 return 1; }); // xには関数オブジェクトの戻り値1が入る。 boost::with_lock_guard(m, [](int n) {}, 2); // with_lock_guardの3番目以降の実引数は、関数オブジェクトに渡される。 |
boost::with_lock_guardの良いところは、排他制御する範囲を明確にできる点です。戻り値の受け渡しが可能なので、関数内に{}でスコープを作ってstd::lock_guardを使うコードでは代用が難しい場合でも、スマートに書ける可能性があります。
class hoge { public: // デフォルトコンストラクタがないクラス。 explicit hoge(int) {} hoge(hoge&&) = default; }; std::mutex m; void f1() { // ここで前処理 hoge obj = boost::with_lock_guard(m, [] { hoge tmp(3); // ここで何か処理 return tmp; }); // ここで残りの処理 } // こういうコードは書けない、という例 void f2() { // ここで前処理 hoge obj; // デフォルトコンストラクタがないので無理! { std::lock_guard<std::mutex> guard(m); hoge tmp(3); // ここで何か処理 obj = std::move(tmp); }; // ここで残りの処理 } |
boost::with_lock_guardを使うには、<boost/thread/with_lock_guard.hpp>をインクルードします。
以下、Boostのリファレンスページへのリンクです。
なお、boost::make_lock_guardは、C++1zで不要になります(参考:C++1z クラステンプレートのテンプレート引数推論 – Faith and Brave – C++で遊ぼう)。
今回は、ロックの補助関数に焦点を当てることにしました。そのため、synchronized_value(boost::synchronized_valueクラス – yohhoyの日記)は取り上げていません。
std::lock_guardだけでなく、これらも使って、楽してコードを書いていきましょう。
スポンサード リンク |
この記事のカテゴリ
- C++ ⇒ Boost.Threadの排他制御を補助する関数