在下面的代码中,x
由于原子线程的限制,线程2中的值始终为10。
int x;
atomic<bool> b(false);
// thread 1:
x = 10;
atomic_thread_fence(memory_order_release);
b = true;
// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(x == 10); // x will always be 10
但是在下面的代码中,*x
线程2中的值始终为10吗?
int* x = new int;
atomic<bool> b(false);
// thread 1:
*x = 10;
atomic_thread_fence(memory_order_release);
b = true;
// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(*x == 10); // will *x always be 10?
在这两种情况下10
,无论是直接完成还是通过指针完成存储,这里都没有区别。
在这里,您不需要内存围栏,因为b = true
本质上是b.store(true, std::memory_order_seq_cst)
-带围栏的获取释放。
这样的内存顺序可防止编译器对操作周围的存储和加载进行重新排序,并使该存储变为可见时,其他线程可以看到前面的存储。
如果比较这两个函数的生成代码:
#include <atomic>
int x;
std::atomic<bool> b(false);
void f() {
x = 10;
std::atomic_thread_fence(std::memory_order_release);
b = true;
}
void g() {
x = 10;
b = true;
}
它是相同的:
f():
movl $10, x(%rip)
movb $1, b(%rip)
mfence
ret
g():
movl $10, x(%rip)
movb $1, b(%rip)
mfence
ret
但是,在您的特定情况下,在我看来,您只需要std::memory_order_release
存储b
即可使该存储x
对其他线程也可见,因此不必使用防护。即b.store(true, std::memory_order_release)
在这里就足够了。消费者代码需要这样做b.load(std::memory_order_acquire)
。
标准互斥锁确实会在锁定时获取内存顺序,并在解锁时释放内存顺序(这是获取/释放术语的位置),因此不涉及任何限制。
很少需要显式的防护,主要是在硬件驱动程序中。在用户空间模式下,由于对C ++ 11内存模型的误解,经常会放置代码围栏。栅栏是最昂贵的原子同步机制,这是避免使用栅栏的主要原因。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句