それは良い質問です。通常は、パブリック インターフェイスを実装から分離し、機能をマルチスレッドの問題から分離することをお勧めします。
私は再帰ミューテックスを使用しており、「_locked」サフィックスなどを含むメソッド名と含まないメソッド名がありました。維持および拡張するのは依然として負担であり、毎回ロックするのは遅かったです(再帰ミューテックスも最悪です)、常にトレースする必要がありました一体何が起こっているのかを把握するためのデータ/呼び出しパス。デッドロックのデバッグは非常に簡単で、保護されていないアクセスの方がはるかに楽しいものでした。
最近では通常、ミューテックスを気にせずにクラスを実装し、「プロキシ」パターンとして実装された壁の後ろにクラスを隠すことで、「ロック解除」アクセスからクラスを保護しています。少なくとも数年間は私にとってはうまく機能していましたが、これまでのところ苦情はありません. このコードはあなたにアイデアを与えるはずです:
#include <cstdio>
#include <mutex>
#include <utility>
class SomeClass {
public:
explicit SomeClass(int v) : v(v) {}
void foo() { printf("\t\tCALLED foo(%d)\n", v); }
void bar() { foo(); printf("\t\tCALL bar(%d)\n", v); }
private:
int v;
};
template <typename T>
class Protector {
std::mutex m_;
T c_;
public:
template <typename ...Args>
Protector(Args && ...args)
: m_(), c_(std::forward<Args>(args)...)
{}
class Interface {
Protector *p_;
Interface(const Interface &) = delete;
Interface & operator = (const Interface &) = delete;
public:
Interface(Protector *p) : p_(p) {
printf("\t+++++ Lock! +++++\n");
p_->m_.lock();
}
Interface(Interface && rhs) : p_(rhs.p_) { rhs.p_ = nullptr; }
T *operator->() { return p_ ? &p_->c_ : nullptr; }
~Interface() {
if (p_) {
printf("\t----- Unlock! -----\n");
p_->m_.unlock();
}
}
};
Interface lock() { return Interface(this); }
Interface operator ->() { return lock(); }
Protector(const Protector &) = delete;
Protector & operator = (const Protector &) = delete;
};
int main()
{
Protector<SomeClass> p(12345);
printf("--*-- Doing batch access! --*--\n");
{
auto c = p.lock();
c->foo();
c->bar();
}
printf("--*-- Doing silly access! --*--\n");
p->foo();
p->bar();
printf("Done!\n");
}
実行例:
$ clang++ -std=c++11 -stdlib=libc++ -O4 -Wall -pedantic -o test ./test.cpp
$ ./test
--*-- Doing batch access! --*--
+++++ Lock! +++++
CALLED foo(12345)
CALLED foo(12345)
CALL bar(12345)
----- Unlock! -----
--*-- Doing silly access! --*--
+++++ Lock! +++++
CALLED foo(12345)
----- Unlock! -----
+++++ Lock! +++++
CALLED foo(12345)
CALL bar(12345)
----- Unlock! -----
Done!
$
それが役に立てば幸い。