これまでは、並列処理が入れ子になっていないことを仮定して task_group_context の詳細については説明していませんでした。ここで、詳細を説明します。
インテル® スレッディング・ビルディング・ブロック (インテル® TBB) のアルゴリズムは、ユーザーがアルゴリズム・テンプレートに提供したコードの断片を実行する task オブジェクトを作成することにより実行されます。デフォルトでは、これらの task オブジェクトはアルゴリズムにより作成される task_group_context と関連付けられます。入れ子のインテル® TBB アルゴリズムは、これらの task_group_context オブジェクトのツリーを作成します。task_group_context をキャンセルすると、その子 task_group_context オブジェクトがすべてキャンセルされ、結果的にそのすべての子孫がキャンセルされます。つまり、アルゴリズムと、そのアルゴリズムが呼び出したアルゴリズムをすべて、1 つのリクエストでキャンセルすることができます。
例外は上向きに伝わります。キャンセルは下向きに伝わります。例外が発生すると、入れ子になっている計算も停止されます。例えば、次の図のツリーについて考えてみます。各ノードはアルゴリズムとその task_group_context を表していると考えてください。
C でアルゴリズムが例外をスローし、例外をキャッチするノードがないことを仮定します。インテル® TBB は、次のように、例外を上方向に伝え、関連するサブツリーを下方向にキャンセルします。
C における例外の制御:
C の例外をキャプチャーします。
C のタスクをキャンセルします。
C から B に例外をスローします。
B における例外の制御:
B の例外をキャプチャーします。
B のタスクをキャンセルし、下向きに D に伝えます。
B から A に例外をスローします。
A における例外の制御:
A の例外をキャプチャーします。
A のタスクをキャンセルし、下向きに E、F、G に伝えます。
A から上向きに例外をスローします。
コードが (任意のレベルで) 例外をキャッチすると、インテル® TBB は例外をそれ以上伝えません。例えば、parallel_for のボディーの外部に例外が伝えられなければ、ほかの反復はキャンセルされません。
キャンセルが下方向にアルゴリズムに伝えられるのを防ぐには、スタックに「孤立した」 task_group_context を構築して、明示的にアルゴリズムに渡します。次のコードの太字の部分です。簡潔に表現するため、サンプルは C++11 ラムダ式を使用しています。
#include "tbb/tbb.h" bool Data[1000][1000]; int main() { try { parallel_for( 0, 1000, 1, []( int i ) { task_group_context root(task_group_context::isolated); parallel_for( 0, 1000, 1, []( int ) { Data[i][j] = true; }, root); throw "oops"; }); } catch(...) { } return 0; }
サンプルは、外部ループ i と内部ループ j の 2 つの並列ループを実行します。孤立した task_group_context root を作成することで、i ループのキャンセルが下方向の内部ループに伝えられなくなります。例外が外部ループに伝えられると、保留中の outer の反復はキャンセルされますが、(開始した外部の反復の) 内部の反復はキャンセルされません。そのため、プログラムが完了したときに、Data の列は反復 i をすべて実行したかどうかに依存して異なりますが、列の内部の要素は false または true のどちらか一方になります。
青の部分を削除すると、内部ループにキャンセルが伝えられるようになります。この場合、Data の列に true と false の両方の値が含まれるようになります。