template<typename handler_type> class aggregator_ext;
#define TBB_PREVIEW_AGGREGATOR 1 #include "tbb/aggregator.h"
aggregator の高度な使用のために、拡張 aggregator インターフェイスが提供されています。このインターフェイスを使用することで、aggregator に渡される操作と aggregator での処理方法をより細かく制御できます。特に、特定の関数を execute メソッドを使用して渡す代わりに、(aggregator_operation から派生した) どのようなデータでも渡すことができる process メソッドがあります (詳細は以下を参照)。さらに、ユーザーは、process メソッドで渡されたデータによって指定された操作を実行するカスタム関数オブジェクトを指定する必要があります。
namespace tbb { class aggregator_operation { public: enum aggregator_operation_status {agg_waiting=0,agg_finished}; aggregator_operation(); void start(); void finish(); aggregator_operation* next(); void set_next(aggregator_operation* n); }; template<typename handler_type> class aggregator_ext { public: aggregator_ext(const handler_type& h); void process(aggregator_operation *op); }; }
メンバー | 説明 |
---|---|
aggregator_ext(const handler_type& h) | 操作の処理にハンドラー h を使用する aggregator_ext オブジェクトを構築します。 |
void process(aggregator_operation* op) | 互いに排他的に実行されるように op の操作に関するデータを aggregator_ext に渡します。op の処理が完了するとリターンします。 |
aggregator_operation::aggregator_operation() | 基本 aggregator_operation オブジェクトを構築します。 |
void aggregator_operation::start() | 処理するための aggregator_operation オブジェクトを準備します。 |
void aggregator_operation::finish() | オリジナルのスレッドへ解放するために aggregator_operation オブジェクトを準備をします。 |
aggregator_operation* aggregator_operation::next() | this の次の aggregator_operation。 |
void aggregator_operation::set_next(aggregator_operation* n) | n を this の次の aggregator_operation にします。 |
次のサンプルは、aggregator_ext を使用して、コンカレントでない std::priority_queue コンテナーを安全に処理します。
typedef priority_queue<value_type, vector<value_type>, compare_type> pq_t; pq_t my_pq; value_type elem = 42; // aggregator_node から派生したデータ class op_data : public aggregator_node public: value_type* elem; bool success, is_push; op_data(value_type* e, bool push=false) : elem(e), success(false), is_push(push) {} }; // aggregator_ext テンプレートに渡すハンドラー class my_handler_t { pq_t *pq; public: my_handler_t() {} my_handler_t(pq_t *pq_) : pq(pq_) {} void operator()(aggregator_node* op_list) { op_data* tmp; while (op_list) { tmp = (op_data*)op_list; op_list = op_list->next(); tmp->start(); if (tmp->is_push) pq->push(*(tmp->elem)); else { if (!pq->empty()) { tmp->success = true; *(tmp->elem) = pq->top(); pq->pop(); } } tmp->finish(); } } }; // aggregator_ext を作成してハンドラー・インスタンスで初期化 aggregator_ext<my_handler_t> my_aggregator(my_handler_t(my_pq)); // 要素をプライオリティー・キューに格納 op_data my_push_op(&elem, true); my_aggregator.process(&my_push_op); // 要素をプライオリティー・キューから取得 bool result; op_data my_pop_op(&elem); my_aggregator.process(&my_pop_op); result = my_pop_op.success;
このサンプルには、重要な点がいくつかあります。最も重要なのはハンドラー・アルゴリズムです。上記に合致していなければなりません。特に、ハンドラーは aggregator_nodes のリンクリストを受け取り、リスト中のすべてのノードを処理する必要があります。任意の順序で処理できますが、ハンドラーがリターンする前にすべてのノードが処理されなければなりません。 リスト中のすべてのノード操作で、aggregator_node に対して next および set_next メソッドを使用するべきです。
さらに、aggregator_node を処理するため、ノードで最初に start メソッドを呼び出す必要があります。そして、あらゆる方法でノードに関連する操作を処理します。処理が完了したら、ノードで finish メソッドを呼び出す必要があります。finish メソッドは、ノードをオリジナルのスレッドに解放し、オリジナルのスレッドによる process メソッドの呼び出しがリターンします。
上記のサンプルで使用されている handler 関数は、このプロセスを最も単純な方法で示しています。各操作を処理するリストを順番にループして、ノードに含まれる情報を処理する前に start を呼び出し、ノードでの処理が完了したら finish を呼び出しています。