次のような単純なバリアをコードに実装しようとしました。
void waitOnBarrier(int* barrier, int numberOfThreads) {
atomicIncrement(barrier); // atomic increment implemented in assembly
while(*barrier < numberOfThreads);
}
そして、コードにはバリアの使用法があります。
int g_barrier = 0; // a global variable
waitOnBarrier(&g_barrier, someKnownNumberOfThreads);
ここまでは順調ですが、g_barrier変数をどこでゼロにリセットすればよいでしょうか? のようなものを書くと
g_barrier = 0;
waitOnBarrier呼び出しの直後に、スレッドの 1 つが他のスレッドよりも速くバリアから解放され、他のすべてのスレッドがまだループ命令を実行している間にg_barrierを無効にすると問題が発生するため、最終的にはバリアに永久にスタックします。 .
説明: waitOnBarrierは次のようなものにコンパイルされます (疑似コード):
1: mov rax, numberOfThreads
2: mov rbx, [barrier]
3: cmp rax, rbx
4: jmp(if smaller) to 2
したがって、バリアで同期している 2 つのスレッドがあり、thread_1が命令 3 または 4 のどこかで遅く、高速のthread_2がバリアに到達し、それを渡し、g_barrier無効化フローに進むとします。つまり、thread_1が命令 2 に到達した後、[barrier] でゼロの値が表示され、永久にバリアにスタックすることになります!
問題は、どのようにg_barrierを無効化する必要があるかということです。その時点までにすべてのスレッドがバリアを離れたことを確認できる「十分に」コード内のどこにあるのでしょうか。または、バリアを実装するより正しい方法はありますか?