データ競合の回避

データ競合の回避#

フローグラフのエッジは、ライブラリーで強制する依存関係を明示的に示します。同様に、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 は常にシリアルに実行されるため、競合は発生しません。