【发布时间】:2015-12-17 02:48:30
【问题描述】:
考虑以下spin_lock() 实现,最初来自this answer:
void spin_lock(volatile bool* lock) {
for (;;) {
// inserts an acquire memory barrier and a compiler barrier
if (!__atomic_test_and_set(lock, __ATOMIC_ACQUIRE))
return;
while (*lock) // no barriers; is it OK?
cpu_relax();
}
}
我已经知道的:
-
volatile防止编译器在while循环的每次迭代中优化出*lock重新读取; -
volatileinserts neither memory nor compiler barriers; - 这样的实现实际上在 GCC 中适用于
x86(例如在 Linux 内核中)和一些其他架构; - 在通用架构的
spin_lock()实现中至少有一个内存和编译器屏障is required;此示例将它们插入到__atomic_test_and_set()。
问题:
-
这里的
volatile是否足够,或者是否有任何架构或编译器在while循环中需要内存或编译器屏障或原子操作?1.1 按照
C++标准?1.2 在实践中,对于已知的架构和编译器,特别是对于 GCC 及其支持的平台?
- 这个实现在 GCC 和 Linux 支持的所有架构上是否安全? (在某些架构上至少效率低下,对吧?)
- 根据
C++11及其内存模型,while循环是否安全?
有几个相关的问题,但我无法从它们中构建出明确明确的答案:
-
Q: Memory barrier in a single thread
原则上:是的,如果程序执行从一个内核移动到下一个内核,它可能不会看到前一个内核上发生的所有写入。
-
Q: memory barrier and cache flush
在几乎所有现代架构上,缓存(如 L1 和 L2 缓存)都由硬件确保一致。无需刷新任何缓存即可使内存对其他 CPU 可见。
Q: Do spin locks always require a memory barrier? Is spinning on a memory barrier expensive?
Q: Do you expect that future CPU generations are not cache coherent?
【问题讨论】:
-
第一个假设不正确 - 在多 CPU 系统上,
volatile读取不能保证同步缓存。它应该用于设备接口,而不是线程。见Volatile and multithreading: is the following thread safe? -
@BoPersson 我相信他只是意味着它强制编译器不要将内存加载操作提升到循环之外,以便它至少会重新读取本地处理器的缓存。问题是是否真的存在这样一种架构,在没有内存屏障的情况下,缓存一致性实际上是一个真正的问题,这意味着 OP 确实理解
volatile不会创建这样的屏障。 -
@davmac,是的!这正是我要问的。
-
作为对 1.2 的部分回答,以下是 LLVM 如何实现易失性和原子内存排序:llvm.org/docs/Atomics.html。所以那里不安全。
-
@g-v gcc 手册的相关部分:gcc.gnu.org/onlinedocs/gcc/Volatiles.html 请注意,g++not 对 volatile 引用做出与 volatile 指针相同的保证。
标签: c++ multithreading gcc memory-barriers spinlock