在 C 中实现线程屏障和屏障重置的正确方法是什么?

use*_*597 5 c x86 multithreading barrier

我尝试在代码中实现一个简单的屏障,如下所示:

void waitOnBarrier(int* barrier, int numberOfThreads) {
    atomicIncrement(barrier); // atomic increment implemented in assembly
    while(*barrier < numberOfThreads);
}
Run Code Online (Sandbox Code Playgroud)

然后代码中有一个屏障的用法:

int g_barrier = 0; // a global variable

waitOnBarrier(&g_barrier, someKnownNumberOfThreads);
Run Code Online (Sandbox Code Playgroud)

到目前为止一切顺利,但是我应该在哪里将g_barrier变量重置为零?如果我写类似的东西

g_barrier = 0;
Run Code Online (Sandbox Code Playgroud)

在waitOnBarrier调用之后,如果其中一个线程比其他线程更快地从屏障中释放并使 g_barrier 无效,所有其他线程仍在执行循环指令,那么我将遇到一个问题,所以最终它们将永远卡在屏障上。

说明: waitOnBarrier将编译成如下内容(伪代码):

1: mov rax, numberOfThreads
2: mov rbx, [barrier]
3: cmp rax, rbx
4: jmp(if smaller) to 2
Run Code Online (Sandbox Code Playgroud)

因此,如果我们有 2 个线程在屏障上同步,并且thread_1在指令 3 或 4 处速度较慢,而更快的thread_2到达屏障,则通过它并继续进入g_barrier无效流程。这意味着在thread_1到达指令 2 后,它将在 [barrier] 处看到一个零值,并且将永远卡在屏障上!

问题是,我应该如何取消 g_barrier 它在代码中的哪个位置“足够远”,以便我可以确定到那时所有线程都离开了屏障?还是有更正确的方法来实现屏障?

R..*_*R.. 3

障碍实际上很难实现,主要原因是新的服务员可以在所有旧的服务员有机会执行之前开始到达,这排除了任何类型的基于简单计数的实现。我的首选解决方案是让屏障对象本身简单地指向一个“当前屏障实例”,该实例存在于第一个到达屏障的线程的堆栈上,并且它也将是最后一个离开的线程(因为它不能在其他线程离开时离开)线程仍在引用其堆栈)。Michael Burr 对我过去关于该主题的问题的回答中包含了一个关于 pthread 原语的非常好的示例实现(可以适用于 C11 锁定原语或您必须使用的任何内容):

/sf/answers/413187001/

是的,看起来工作量很大,但是编写一个真正满足屏障契约的屏障实现并非易事。