10

我正在阅读关于 volatile 关键字的这个问题的答案:

https://stackoverflow.com/a/2485177/997112

该人说:

防止重新排序的解决方案是使用内存屏障,它向编译器和 CPU 都表明,在这一点上不能对内存访问进行重新排序。在我们的 volatile 变量访问周围放置这样的障碍可以确保即使是非 volatile 访问也不会在 volatile 中重新排序,从而允许我们编写线程安全的代码。

但是,内存屏障还确保在达到屏障时执行所有挂起的读/写操作,因此它有效地为我们提供了我们需要的一切,从而使 volatile 变得不必要。我们可以完全删除 volatile 限定符。

这个“内存屏障”是如何在 C++ 中实现的?

编辑:

有人可以给出一个简单的代码示例吗?

4

3 回答 3

10

在 C++11 中使用内存屏障很简单:

std::atomic<int> i;

所有访问i都将受到内存屏障的保护。

于 2013-07-27T17:12:49.683 回答
7

这是非常依赖硬件的。从相当长的 Linux 内核内存屏障文档中:

The Linux kernel has eight basic CPU memory barriers:

TYPE                MANDATORY               SMP CONDITIONAL
===============     ======================= ===========================
GENERAL             mb()                    smp_mb()    
WRITE               wmb()                   smp_wmb()
READ                rmb()                   smp_rmb()   
DATA DEPENDENCY     read_barrier_depends()  smp_read_barrier_depends()

让我们特别考虑其中之一smp_mb():如果你打开asm/x86/um/asm/barrier.h,你会发现whenCONFIG_SMP 是定义的,

#define smp_mb()    mb()

如果你向上滚动,你可以看到根据平台的不同,mb 有不同的实现:

// on x86-32
#define mb()        alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)
// on other platforms
#define mb()        asm volatile("mfence" : : : "memory")

有关这 2 件事之间差异的更多信息已在此线程中进行了讨论。我希望这有帮助。

于 2013-07-27T13:36:39.640 回答
2

通常,有“内在函数”——这些是编译器对它们如何操作具有特殊知识的特殊函数(特别是它们是内存屏障)。名称因编译器而异(有时对于同一编译器的不同架构)。

例如,MSVC 使用_ReadBarrier, WriteBarrier_ReadWriteBarrier

lfence在 x86 中,它会产生一个sfencemfence指令——分别执行“加载”、“存储”和“所有内存操作”屏障——换句话说,anlfence将是内存读取操作的屏障,ansfence将是“内存write" 屏障,并且mfence将成为读取和写入操作的屏障。

于 2013-07-27T19:06:44.820 回答