タスク・スケジューラー

インテル® スレッディング・ビルディング・ブロック (インテル® TBB) は、アルゴリズム・テンプレートとタスクグループを動かすエンジンであるタスク・スケジューラーを提供します。直接呼び出すことも可能です。タスク・スケジューラーは多くの処理を行うため、タスクの使用は多くの場合、スレッドの使用よりも単純で効率的です。

タスクは計算の論理ユニットです。スケジューラーは、物理スレッドにタスクをマップします。マッピングはノンプリエンプティブです。各スレッドには execute() メソッドがあり、スレッドが execute() の実行を開始すると、execute() がリターンするまで、タスクはスレッドにバインドされます。その間、スレッドはプレデセッサー・タスクを待っている場合のみほかのタスクを処理します。プレデセッサー・タスクを実行するか、保留中のプレデセッサー・タスクがない場合、ほかのスレッドで作成されたサービスタスクを実行します。

タスク・スケジューラーは、計算主体の処理を並列化するように意図されています。タスク・オブジェクトはプリエンプティブにスケジュールされないため(その間、ブロックされたスレッドはほかのタスクを処理できないため)、スレッドを長い間ブロックする呼び出しを行ってはなりません。

注意

スケジューラーは利用可能なワーカースレッドに収まるように実際の並列処理を調整するため、潜在的に 並列可能なタスクが実際に 並列実行されるという保証はありません。例えば、単一のワーカースレッドが提供された場合、スケジューラーは並列処理を作成できません。さらに、生産タスクの実行中に消費タスクがこの作業を行う保証がないため、生産タスクと消費タスクの関係でタスクを使用することは安全ではありません。

潜在的な並列処理は通常、分割/結合 パターンによって構成され、2 つの基本パターンがサポートされています。最も効率的なのは、プログラマーが明示的に "継続" タスクを構築する継続渡しです。親は子タスクを生成して、子が完了するときに実行する継続タスクを指定します。継続は親の先祖を継承します。その後、親タスクは終了します。つまり、子をブロックしません。続いて子が実行され、子 (またはその継続) が終了した後、継続タスクが実行を開始します。次の図は、一連のステップを示しています。各ステップの実行タスクは色付きで表示されています。

継続渡しスタイル

明示的な継続渡しは、タスクからスレッドのスタックを分離するので効率的です。しかし、プログラムはより難しくなります。2 つ目のパターンは、暗黙的な継続を使用する "ブロックスタイル" です。このパターンはパフォーマンスの点では効率が落ちますが、プログラムはより簡単です。このパターンでは、下記の図で示されているように、親タスクはその子が完了するまでブロックします。

ブロックスタイル

便利さには代償がつきものです。親がブロックしているため、そのスレッドのスタックをまだポップできません。連続したスチールとブロックはスタックを境界なしで拡張させるため、スレッドがどの作業を受け持つか注意する必要があります。この問題を解決するため、スケジューラーは最も深くブロックされたタスクよりも浅く、タスクを実行しないブロックされたスレッドを制約します。この制約により、利用可能な並列処理が制限され、スレッドがほかの方法で選択するサブツリーよりも小さな (深い) サブツリーを選択するため、パフォーマンスに影響します。

関連情報