注意:我不是这个主题的专家,我的一些陈述是“我在互联网上听到的”,但我想我仍然可以澄清一些误解。
[编辑]一般来说,我会依赖平台特定的东西,例如 x86 原子读取和缺乏 OOOX 仅在由 #ifdef 检查目标平台的隔离的本地优化中,理想情况下伴随着#else 路径中的便携式解决方案。
注意事项
- 读/写操作的原子性
- 由于编译器优化而重新排序(这包括由于简单的寄存器缓存而被另一个线程看到的不同顺序)
- CPU 中的乱序执行
可能的误解
1. 据我了解,因为函数受到保护,内部编译器无法重新排序。
[编辑] 澄清一下:_ReadWriteBarrier 提供了防止指令重新排序的保护,但是,您必须超越函数的范围。 _ReadWriteBarrier 已在 VS 2010 中修复,早期版本可能会被破坏(取决于它们实际所做的优化)。
优化不仅限于功能。有多种机制(自动内联、链接时间代码生成)跨越函数甚至编译单元(并且可以提供比小范围寄存器缓存更重要的优化)。
2. Visual C++ [...] 使 volatile 读写原子加载和存储,
你是在哪里找到那个东西的。 MSDN 表示超出标准,会在读写周围设置内存屏障,不保证原子读取。
[edit]请注意,C#、Java、Delphi 等具有不同的内存 mdoel,可能会做出不同的保证。
3. 在 x86 上,普通的加载和存储应该是原子的,对吧?
不,他们不是。未对齐的读取不是原子的。如果它们对齐良好,它们恰好是原子的——除非它被隔离且易于交换,否则我不会依赖这一事实。否则,您的“x86 简化”将成为对该目标的锁定。
[edit]发生未对齐的读取:
char * c = new char[sizeof(int)+1];
load(*(int *)c); // allowed by standard to be unaligned
load(*(int *)(c+1)); // unaligned with most allocators
#pragma pack(push,1)
struct
{
char c;
int i;
} foo;
load(foo.i); // caller said so
#pragma pack(pop)
如果您记得参数必须对齐并且您控制所有代码,那么这当然是所有学术性的。我不会再写这样的代码了,因为我经常被过去的懒惰所困扰。
4. 普通加载在 x86 上具有获取语义,普通存储具有释放语义
不,x86 处理器不使用乱序执行(或者更确切地说,没有可见的 OOOX - 我认为),但这并不能阻止优化器重新排序指令。
5. _ReadBarrier / _WriteBarrier / _ReadWriteBarrier 做所有的魔法
他们没有 - 他们只是阻止优化器重新排序。 MSDN 最终将其设为 VS2010 的 big bad warning,但信息显然适用于 previous versions as well。
现在,回答您的问题。
我假设 sn-p 的目的是传递任何变量 N,并加载它(原子地?)直接的选择是互锁读取或(在 Visual C++ 2005 及更高版本上)易失性读取。
否则在读取之前您需要为编译器和 CPU 设置一个屏障,在 VC++ 客厅中,这将是:
int load(int& var)
{
// force Optimizer to complete all memory writes:
// (Note that this had issues before VC++ 2010)
_WriteBarrier();
// force CPU to settle all pending read/writes, and not to start new ones:
MemoryBarrier();
// now, read.
int value = var;
return value;
}
没有,_WriteBarrier 在 MSDN 中有第二个警告:
*在 Visual C++ 编译器的过去版本中,_ReadWriteBarrier 和 _WriteBarrier 函数仅在本地强制执行,不会影响调用树上的函数。这些函数现在在调用树中一直强制执行。*
我希望这是正确的。 stackoverflowers,如果我错了,请纠正我。