この記事は、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のリファレンスページへのリンクです。

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だけでなく、これらも使って、楽してコードを書いていきましょう。


スポンサード リンク

この記事のカテゴリ

  • ⇒ Boost.Threadの排他制御を補助する関数