これまで自分で陽にマルチスレッドのコードを書くときは、
pthread や ROOT の TThread を使っていたのだが、
今回は c++11 以降でサポートされた std::thread で書いてみた。
std::thread
thread で処理する関数と、その関数に渡す引数を与えて、新しいスレッド作成。
複数の thread を順に作って、一斉に走らせたいときは、
関数の頭で後述する condition_variable の wait を呼んで、
同 notify(_all) を待機させておけば良い。
スレッドを作成したら、join か detach をさせるのを忘れずに。
std::mutex
mutex 本体、単独で直接使うことはあまりないはず。
以下のいずれかの形式で扱う。
std::lock_guard
コンストラクタで mutex をロック、デストラクタで解除する。
{} 内のローカル変数として定義して自動解除させるように使う。
単純なだけあって後述の std::unique_lock よりオーバーヘッドが少ないようだ。
1 2 3 4 5 |
std::mutex mtx; { std::lock_guard<std::mutex> lock(mtx); ... } |
std::unique_lock
任意のタイミングでロックできるなど、前述の std::lock_guard の高機能版。
高機能なだけあって、実行コストは大きめのようだ。
std::condition_variable::wait(...) を仕掛けられるのはこちら。
std::condition_variable
pthread_cond_* とか TCondition に対応する。
wait/signal などの同期制御に相応する。
1 2 3 4 5 6 7 |
std::mutex mtx; std::condition_variable cv; ... { std::unique_lock<std::mutex> lk(mtx); cv.wait(lk,[&]{ return b_notify; }); } |
と仕掛けておいて、
1 2 3 4 5 |
{ std::lock_guard<std::mutex> lk(mtx); b_notify=true; cv.notify_all(); } |
で一斉に起こすという感じ。
b_notify
の設定は、Sprious Wakeup 対策として必要。
活用
高速でマルチスレッドなデータ取得用プログラムを作成する際に活用。
以下のようにしてスレッドを割り当て。
- メインスレッド
- 読み出し機器毎
- I/O (主にディスク出力)
- オンラインモニター
ちなみに前回の記事は、
このデータ取得プログラムの ROOTファイル出力に zlib より高速な LZ4 圧縮を試すため。
残念ながら LZ4 でも間に合わないので、ひとまず ramdisk に無圧縮で出力することにした。