同期

タスク t を作成すると、t.execute() を呼び出すスレッドが呼び出されるか、t がレディプールに格納されます。その後、タスク・スケジュールに関与しているスレッドがタスクを取得して、t.execute() を呼び出します。「スケジュール・アルゴリズム」セクションで、レディプールの構造を説明しています。

タスクを生成する呼び出しには 2 つの形式があります。

一部の呼び出しは、生成されるルートタスクとルート以外のタスクを識別します。ルートタスクは、allocate_root メソッドを使用して作成されます。

set_ref_count メソッドを呼び出して、プレデセッサーの数と "wait_for_all" メソッドの使用が明らかになるまで、タスクはプレデセッサー・タスクを生成しません。

次の表は、このテンプレート・クラスのメンバーの詳細な情報を提供します。
メンバー 説明
void set_ref_count( int count )

要件: count>=0。連続で n のプレデセッサーを生成して待つ場合、countn+1 であること。その他の場合、count が n であること。

効果: refcount 属性を count に設定します。

int add_ref_count( int count )

refcount 属性に count をアトミックに追加します。

戻り値: refcount 属性の新しい値。

void increment_ref_count();

refcount 属性をアトミックにインクリメントします。

int decrement_ref_count();

refcount 属性をアトミックにデクリメントします。

戻り値: refcount 属性の新しい値。

add_ref_countincrement_ref_count、および decrement_ref_count の明示的な使用は通常、タスクに 2 つ以上のサクセサータスクがある場合にのみ必要です。詳細は、チュートリアルの「タスクの一般的な非循環グラフ」セクションで説明しています。

void wait_for_all()

要件: refcount=n+1。ここで、n はまだ実行しているプレデセッサーの数。

効果: refcount が 1 になるまでレディプールのタスクを実行します。その後、タスクの task_group_contextconcurrent_wait を指定する場合は refcount=1 のまま変更しません。その他の場合、refcount を 0 に設定します。下記の図は、状態遷移を要約したものです。

wait_for_all() は、次の条件がすべて満たされる場合、タスクと暗黙的に関連する task_group_context のキャンセル状態を自動的にリセットします。

  • コンテキストを指定しないでタスクが割り当てられた。

  • 呼び出しスレッドが、インテル® スレッディング・ビルディング・ブロック (インテル® TBB) のワーカースレッドではなく、ユーザーが作成したスレッドである。

  • スレッドによる wait_for_all() の最も外側の呼び出しである。

ヒント

上記の条件下では、task_group_context がキャンセルされた場合に、後から状態を知る方法はありません。状態を知る必要がある場合は、明示的に task_group_context を使用してください。

wait_for_all の効果

k=0 (デフォルト)

k=1 (対応する task_group_contextconcurrent_wait を指定する場合)

static void spawn( task& t )

タスク t をレディプールに格納して直ちにリターンします。

tsuccessor が NULL でない場合、子タスクが完了すると successor.refcount が非同期にデクリメントされるため、successor は子タスクが作成される前に set_ref_count を呼び出す必要があります。ライブラリーのデバッグバージョンは、set_ref_count への必要な呼び出しが行われなかったり、または遅すぎる場合を検出します。

static void spawn ( task_list& list )

list の各タスクで spawn を実行して list をクリアするのと等価ですが、より効率的です。list が空の場合、何も起こりません。

タスクは個々にスチールされるため、list が長い場合、ボトルネックが発生する可能性があります。ボトルネックが発生する場合は、代わりに再帰パターンや並列ループ・テンプレートを使用して、多くの個々のワークに分割することを検討してください。

void spawn_and_wait_for_all( task& t )

要件: this のほかのプレデセッサーがすでに作成されていること。タスク t が NULL 以外の successor 属性を含むこと。 t から呼び出しタスクまで successor リンクのチェーンがあること。 通常、このチェーンは単一のリンクを含みます。つまり、tthis のプレデセッサーです。

効果: {spawn(t); wait_for_all();} に似ていますが、多くの場合より効率的です。さらに、task が現在のスレッドによって実行されることを保証します。この制約は、同期を単純化する場合があります。下記の図は、状態遷移を要約したものです。上記の図に似ていますが、タスク tn 番目のタスクです。

spawn_and_wait_for_all の効果

k=0 (デフォルト)

k=1 (対応する task_group_contextconcurrent_wait を指定する場合)

void spawn_and_wait_for_all( task_list& list )

{spawn(list); wait_for_all();} に似ていますが、多くの場合より効率的です。

static void spawn_root_and_wait( task& root )

要件: タスク root のメモリーが task::allocate_root() で割り当てられていること。

効果: rootparent 属性に未定義の値を設定して、「execute() の処理」セクションで説明されているように root を実行します。root が再利用されていなければ、後で root を破棄します。

static void spawn_root_and_wait( task_list& root_list )

要件: root_list の各タスク・オブジェクト tstatic void spawn_root_and_wait( task& root ) の要件を満たしていること。

効果: root_list の各タスク・オブジェクト t で、spawn_root_and_wait(t) を並列に実行します。「static void spawn_root_and_wait( task& root )」セクションで、spawn_root_and_wait(t) の動作を説明しています。

static void enqueue ( task& )

タスクの完了を明示的に待っているスレッドがない場合でも、タスクはワーカースレッドにより最終的に実行されるようにスケジュールされます。ワーカースレッドの合計が 0 の場合、キューに入れられたタスクを実行する追加の特別なワーカースレッドが作成されます。

キューに入れられたタスクは、先着順に処理されます。

注意

再帰並列処理でキューに入れられたタスクを使用すると、再帰が幅優先で行われるため、メモリーの使用量が非常に多くなります。再帰並列処理には、通常の spawn メソッドを使用してください。

注意

プログラムの無関係な部分からほかのキューに入れられたタスクを先に処理しなければならない可能性があるため、キューに入れられたタスクを明示的に待つことは避けてください。キューに入れられたタスクの使用を推奨するパターンは、例えば、タスクをキューに入れたスレッドにメッセージを送ることにより、タスクの完了を非同期に知る方法です。例については、インテル® TBB の「デザインパターン」を参照してください。

関連情報