フローグラフのメッセージ数を制限する柔軟なソリューションは、トークンを使用することです。トークンベース・システムでは、グラフで利用可能なトークンの数が限られており、メッセージはトークンとペアになるまでグラフに入ることができません。メッセージがグラフからリタイアされると、そのトークンが解放され、新しいメッセージとペアになり、グラフに入ることができます。
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 を使用して、トークンをリサイクルするかしないか、より多くのトークンを生成するかを選択し、グラフで許可される同時実行数を増減できます。
このため、トークンベース・システムは非常に柔軟です。トークンを任意の種類で宣言し、実行とともにシステムに挿入あるいはシステムから削除して、システムで許可される同時実行数を動的に制御できます。ソースでトークンを入力とペアにできるため、このアプローチにより、グラフ全体のリソース消費を制限することができます。