データ競合の回避#
フローグラフのエッジは、ライブラリーで強制する依存関係を明示的に示します。同様に、function_node および multifunction_node オブジェクトの同時実行制限により、ランタイム・ライブラリーが許可する同時呼び出しの最大数が制限されます。これらはライブラリーによって強制される制限であり、ライブラリーは自動的にデータ競合から保護するものではありません。これらのメカニズムを使用して、データ競合を明示的に回避する必要があります。
例えば、次のコードでは、ノード f で参照されるグローバル count オブジェクトへの同時アクセスを防ぐものがないため、データ競合が発生します。
graph g;
int src_count = 1;
int global_sum = 0;
int limit = 100000;
input_node< int > src( g, [&]( oneapi::tbb::flow_control& fc ) -> int {
if ( src_count <= limit ) {
return src_count++;
} else {
fc.stop();
return int();
}
} );
src.activate();
function_node< int, int > f( g, unlimited, [&]( int i ) -> int {
global_sum += i; // data race on global_sum
return i;
} );
make_edge( src, f );
g.wait_for_all();
cout << "global sum = " << global_sum
<< " and closed form = " << limit*(limit+1)/2 << "\n";上記の例を実行すると、データ競合により、予想される結果よりも少し小さいグローバル合計が計算される可能性があります。この例では、f で許可される同時実行性を無制限から 1 に変更し、各値が f によって順番に処理されるようにすることで、データ競合を回避できます。また、input_node はグローバル値 src_count も更新することに注意してください。ただし、input_node は常にシリアルに実行されるため、競合は発生しません。