フローグラフのメッセージ数を制限する柔軟なソリューションは、トークンを使用することです。トークンベース・システムでは、グラフで利用可能なトークンの数が限られており、メッセージはトークンとペアになるまでグラフに入ることができません。メッセージがグラフからリタイアされると、そのトークンが解放され、新しいメッセージとペアになり、グラフに入ることができます。
tbb::pipeline および tbb::parallel_pipeline アルゴリズムはトークンベース・システムに依存します。フローグラフのインターフェイスはトークンを明示的にサポートしていませんが、join_node を使用して類似のシステムを作成できます。join_node には、2 つのテンプレート引数、入力の種類を記述するタプル、バッファーポリシーがあります。
template<typename OutputTuple, graph_buffer_policy JP = queueing> class join_node;
バッファーポリシーは次のいずれかになります。
下記の例では、M ビッグ・オブジェクトを生成する source_node と、3 つのトークンがあらかじめ設定された buffer_node があります。token_t は何にでも (例えば、typedef int token_t; に) できます。source_node と buffer_node は、reserving join_node に接続されます。reserving join_node により取得されると、source_node は入力を生成します。また、reserving join_node は、buffer_node から取得するアイテムがあることを知っている場合は、source_node から入力を取得します。
graph g;
int src_count = 0;
int number_of_objects = 0;
int max_objects = 3;
source_node< big_object * > s( g, [&]( big_object* &v ) -> bool {
if ( src_count < M ) {
v = new big_object();
++src_count;
return true;
} else {
return false;
}
} );
join_node< tuple_t, reserving > j(g);
buffer_node< token_t > b(g);
function_node< tuple_t, token_t > f( g, unlimited,
[]( const tuple_t &t ) -> token_t {
spin_for(1);
cout << get<1>(t) << "\n";
delete get<0>(t);
return get<1>(t);
} );
make_edge( s, input_port<0>(j) );
make_edge( b, input_port<1>(j) );
make_edge( j, f );
make_edge( f, b );
b.try_put( 1 );
b.try_put( 2 );
b.try_put( 3 );
g.wait_for_all();
上記のコードで、function_node はトークンを buffer_node に戻しています。このフローグラフのサイクルにより、トークンがリサイクルされ、source_node の別の入力とペアになることができます。そのため、前のセクションのように、グラフには最大 4 つのビッグ・オブジェクトがあります。function_node に 3 つのビッグ・オブジェクト、source_node にバッファーされる 1 つのビッグ・オブジェクトがあり、ペアになるトークンを待機します。
フローグラフ用に定義された特定の token_t はないため、オブジェクトや配列へのポインターを含む、任意の型をトークンに使用できます。そのため、上記の例とは異なり、token_t は仮の型である必要はなく、例えば、計算に不可欠なバッファーやその他のオブジェクトでもかまいません。例えば、ビッグ・オブジェクト自身をトークンとして使用するように上記の例を変更すると、ビッグ・オブジェクトの割り当てと割り当て解除の繰り返し、そして buffer_node に戻すサイクルを利用してビッグ・オブジェクトのフリーリストを作成する必要性がなくなります。
また、上記の例で、buffer_node は固定数の try_put の明示的な呼び出しによりあらかじめ設定されていますが、ほかの方法もあります。例えば、source_node は buffer_node の入力にアタッチできます。また、トークンを生成できます。さらに、function_node は、出力ポートに 0 以上の出力を送る multifunction_node に置換できます。multifunction_node を使用して、トークンをリサイクルするかしないか、より多くのトークンを生成するかを選択し、グラフで許可される同時実行数を増減できます。
このため、トークンベース・システムは非常に柔軟です。トークンを任意の種類で宣言し、実行とともにシステムに挿入あるいはシステムから削除して、システムで許可される同時実行数を動的に制御できます。ソースでトークンを入力とペアにできるため、このアプローチにより、グラフ全体のリソース消費を制限することができます。