スレッド間で共有する変数のアクセス権制御を C++ コンパイラで強制する方法
マルチスレッドなプログラムを書いていると、スレッド間で共有する変数へのアクセスを正しく直列化できているか、という点が常に問題になります。どうせなら、正しく書けているかコンパイル時に確認したいよね、ということで、以下のような C++ テンプレートを書いてみました。
template <typename T> class cac_mutex_t {
public:
class lockref {
protected:
cac_mutex_t<T>* m_;
public:
lockref(cac_mutex_t<T>& m) : m_(&m) {
pthread_mutex_lock(&m_->mutex_);
}
~lockref() {
pthread_mutex_unlock(&m_->mutex_);
}
T& operator*() { return m_->t_; }
T* operator->() { return &operator*(); }
private:
lockref(const lockref&);
lockref& operator=(const lockref&);
};
protected:
friend class cac_mutex_t<T>::lockref;
T t_;
pthread_mutex_t mutex_;
public:
cac_mutex_t(pthread_mutexattr_t* attr) : t_() {
pthread_mutex_init(&mutex_, attr);
}
~cac_mutex_t() {
pthread_mutex_destroy(&mutex_);
}
const T* unsafe_ref() const { return &t_; }
T* unsafe_ref() { return &t_; }
private:
cac_mutex_t(const cac_mutex_t&);
cac_mutex_t& operator=(const cac_mutex_t&);
};
使用例は以下のとおり。
// mutex lock が必須な Foo 型の変数を宣言
cac_mutex_t<Foo> foo_serialized(NULL);
{ // RAII で mutex lock してアクセス
cac_mutex_t<Foo>::lockref foo(foo_serialized);
foo->x = ...
...
}
要は、RAII かつスマートポインタなクラスを使って、ロックをコントロールしつつ、アクセスの直列化を保証できるよ、ということです。
テクニックとしては既存だと思いますが、ちょっとググった範囲では見つからなかったので、紹介させていただきました。
6月24日追記: 設計を整理、rwlock を追加して Q4M の svn に追加しました。(link)
Comments