同期

タスク 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 に設定します。

void increment_ref_count();

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

int decrement_ref_count();

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

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

increment_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 の効果
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 の効果
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 の「デザインパターン」を参照してください。

関連情報