#include <thread> #include <mutex> #include <queue> #include <unistd.h> // usleep using namespace std; mutex print_mutex; // printf()呼び出し排他用 mutex queue_mutex; // v_queue排他アクセス用 queue<int> v_queue; // データキュー condition_variable ready_cond; // 条件変数 void worker(void) { while (true) { int data; { // unique_lockではコンストラクタでロックを取得し、デストラクタでロックを解除する。 // また、明示的にロック、アンロックが可能である。 unique_lock<mutex> lk(queue_mutex); // キューにデータがない場合はキューにデータが追加されたことが // 通知されるまで待つ。CPUを余分に消費することがない。 while (v_queue.empty()) { // waitを呼ぶ前にはlkがロック状態でなければならない。 // C++11でもspurious wakeupの問題があることに注意。 // ここでready_cond.notify_one()が呼ばれるまでブロックする。 // ready_cond.notify_one()が呼ばれると、再びロックを取得した状態で、この関数から抜ける。 ready_cond.wait(lk); } // キューからデータを取り出す data = v_queue.front(); v_queue.pop(); } lock_guard<mutex> l(print_mutex); printf("%p %d\n", this_thread::get_id(), data); fflush(stdout); } } int main(int argc, const char * argv[]) { thread a(worker); thread b(worker); int i = 0; while (true) { // キューにデータを追加する { lock_guard<mutex> l(queue_mutex); v_queue.push(i++); } // ready_cond変数でwaitしているスレッドのどれか一つのロックを解除する。 ready_cond.notify_one(); // 1sec待つ usleep(1000000); } return 0; }
実行結果
0x100281000 0 0x100304000 1 0x100281000 2 0x100304000 3 0x100281000 4 0x100304000 5 0x100281000 6 0x100304000 7 0x100281000 8 0x100304000 9
1秒毎に結果が表示されている。
今回はたまたま、2つのスレッドが交互にロック解除されているが、
どのスレッドがロックを取得するかに、決まりはない(はず)。
ここでは2つのスレッドを停止させる処理を作っていないが、作る場合は、
notify_one()のほかにnotify_all()という関数で、ロック状態にあるスレッドの全てのwait()を返すことができるので、これを使う。
<参考文献>
C言語で実装しなければならない場合はこちら。
Programming with POSIX Threads / David R. Buternhof / Addison Wesley
Programming with POSIX Threads (Addison-Wesley Professional Computing Series) (English Edition)
- 作者: David R. Butenhof
- 出版社/メーカー: Addison-Wesley Professional
- 発売日: 1993/05/15
- メディア: Kindle版
- この商品を含むブログを見る
C++11を使った並列処理。
最近、C++14/17に対応した改訂版が出たようです。
C++ Concurrency IN ACTION / Anthony Williams / MANNING
- 作者: Anthony Williams
- 出版社/メーカー: Manning Publications
- 発売日: 2019/02/10
- メディア: ペーパーバック
- この商品を含むブログを見る