C++でJavaっぽい、いやJava以上のsynchronized

More than 3 years have passed since last update.

C++11以降だとstd::mutexとstd::lock_guardがありますし、これらは簡単なクラスだから古いC++でも数秒で自作できますし、でも排他スコープが明確なJava/C#のsynchronizedの記法がうらやましいなーとこんなマクロ書いちゃうこと多いですよね。(よね?)

#define synchronized(monitor) \

if (auto __lock = std::make_unique<std::lock_guard<std::mutex>>(monitor))

こういうの定義しておくと

std::vector<int> data_;  // いろんなスレッドから読み書きするデータ

std::mutex monitor_; // mutexオブジェクトを宣言しておいて

...

synchronized (motinor_) {
// 排他制御された処理
data_.doSomething();
}

Javaやあっ。

さてここからはJavaを超えに行きます。

プログラマの意図は「data_を自スレッドしか触っていないことを保証したい」であって、monitor_という存在は意図よりも低レベル、ノイズです。Javaだとdata_そのものにモニタの役割もさせてしまう(synchronized(data_)と書く)こともまあできますが、これはあまりよろしくない技法だから普通はmonitor_を別に用意するものです。

C++ならそれもマクロに用意させてプログラマの目から隠せる。

// 先ほどのsynchronizedの定義に追加します。

// モニタ変数の名前は _mutex_【対象変数名】 だ
#define MONITOR_OF(var) _mutex_ ## var

// 変数宣言とそれ専用のモニタ変数宣言をやってくれる
#define DECLARE_THREAD_SAFE(type, var) \
type var; \
std::mutex MONITOR_OF(var);

// 独占使用したい変数を指定する記法
#define exclusive(var) synchronized(MONITOR_OF(var))

こうすると、data_の独占使用はこうなります。

DECLARE_THREAD_SAFE(std::vector<int>, data_)  // いろんなスレッドから読み書きするデータ

...

exclusive (data_) {
// 排他制御された処理
data_.doSomething();
}

synchronizedマクロですが、if構文の枠に乗せるためにmake_uniqueしてるのがなんか迂遠で汚らしいですね。なんかいい書き方ありますかね。ifでなくfor使うとか?