【发布时间】:2014-12-01 15:03:36
【问题描述】:
我尝试在我的代码中实现一个简单的屏障,如下所示:
void waitOnBarrier(int* barrier, int numberOfThreads) {
atomicIncrement(barrier); // atomic increment implemented in assembly
while(*barrier < numberOfThreads);
}
然后代码中有一个barrier的用法:
int g_barrier = 0; // a global variable
waitOnBarrier(&g_barrier, someKnownNumberOfThreads);
到目前为止一切顺利,但我应该在哪里将我的 g_barrier 变量重置为零?如果我写类似
g_barrier = 0;
在 waitOnBarrier 调用之后,如果其中一个线程比其他线程更快地从屏障中释放并取消 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,它在代码中的哪个位置“足够远”,我可以确定到那时所有线程都离开了屏障?还是有更正确的方法来实现屏障?
【问题讨论】:
-
您是否有不想使用操作系统提供的线程同步库的原因?
-
是的,我的代码运行在特定的环境中,而不是传统的操作系统。
-
实现这种无锁有什么特别的原因吗?
-
自 C11 起,C 将原子类型作为语言的一部分,编译器开始实现它。如果您的编译器没有,我认为仍然值得使用该标准带来的概念。特别是你应该使用类似于
atomic_flag的东西和测试和设置操作来实现忙等待。不要重新发明轮子,让您的代码面向未来。
标签: c multithreading x86 barrier