C++ 組み込みの ActiveObject 機構
http://d.hatena.ne.jp/kmt-t/20060109#1136757641 経由で
http://216.55.183.63/pdc2005/slides/TLN309_Sutter.ppt
このスライドは PDC2005 での "C++: Future Directions in Language Innovation" というセッションのもの。前後の文脈が完全に分かっていないのですが、C++ に将来的にこんな仕様を入れてやろう、というセッションだった模様です。
このスライドでは concurrency に関する記述が多いのですが、Concur というプロジェクトの成果らしき、active 修飾子を使った一連の仕様が興味深かったのでメモ。
active class Service1 { public: int Operation1(); }; active class Service2 { public: int Operation2(); }; void Process( active Service1& s1, active Service2& s2, int ms ) { future<int> result1 = s1.Operation1(); future<int> result2 = s2.Operation2(); wait( (result1 && result2) || timeout(ms) ); if( result1 && result2 ) { … // use result1 and result2 else { result1.Cancel(); // cancel both result2.Cancel(); } }
active がついているクラスのメソッドは、返り値の型が T だと実際には future
active なクラスは内部的に処理待ちキューを持っています。このクラスへのメソッド呼び出しは自動的に処理待ちキューへ詰まれて、呼び出し自身はすぐに返ります。そして、メソッド呼び出しの返り値に対して wait() が呼び出されるまで、呼び出し元ではブロックは発生しない、という仕組みです。処理待ちキューでシリアライズされるため、クラス内では改めてロックを取らなくてもよいことも大きな利点。
なお、wait() を呼び出す以外でも、スコープから外れたなどでデストラクタが呼び出されるタイミングでも wait() と同等の処理が行われるようです。
分散系のデザインパターンには詳しくないのですが、ActiveObject パターンと Future パターンというものを言語に組み込みにしたもの、という理解でいいんでしょうか。
C++ にλ式を組み込むと、あわせ技で以下のようなこともできるようになります。(λ式といいながら、以下の例ではただのブロックですが……)
x = active { foo(10) }; // call foo asynchronously y = active { a->b( c ) }; // evaluate asynchronously p = active { new T }; // allocate and construct asynchronously … // 上の3つの active lambda 式と並行で実行できるコードを書きます。 // wait() が呼ばれるまで、待ちは発生しません。 return x.wait() * y.wait() * p.wait()->bar();
このくらいの簡潔さであれば、プログラマに書かせてもあまり気になりませんよね。
ついでに、OpenMP の #pragma omp parallel for と同等のことを行う active for each なんていう構文もあるようです。
ただ、個人的には OpenMP などで実現できる細粒度なマルチスレッドやこの手の中粒度なマルチスレッドではオーバーヘッドが大きいのではないかと感じています。一般の応用アプリケーションでは、もう少し大きく、ずっと並列に走り続けられるようなタスクに分割してスレッド化したほうがいいのではないかと思うのですが、どうなんでしょ。
将来的に一般的なマシンの並列度が100とか1000とかなってきたら、このくらいの中粒度のタスクをスレッドプールに投げ込む感じのプログラムがよくなってくるんでしょうかね〜。
それにしても、future