我一直在使用64位项目分配给VS2017上的atomic_long
类型时介入涉及的函数调用。我特别想看看将atomic_long
复制到非原子变量时会发生什么,如果周围有任何锁定。
atomic_long ll = 10;
long t2 = ll;
最终以此调用结束(我已删除了一些被 ifdef
ed的代码(
inline _Uint4_t _Load_seq_cst_4(volatile _Uint4_t *_Tgt)
{ /* load from *_Tgt atomically with
sequentially consistent memory order */
_Uint4_t _Value;
_Value = *_Tgt;
_Compiler_barrier();
return (_Value);
}
现在,我已经从MSDN读到,对32位值的平整读取将是原子:
简单的读取和写入正确对齐的32位变量是 原子操作。
...这说明了为什么只有Interlocked
函数仅用于阅读;只有用于更改/比较的人。我想知道的是_Compiler_barrier()
位在做什么。这是
#define
d __MACHINE(void _ReadWriteBarrier(void))
...我又在MSDN上发现了这个
限制了可以重新排序内存访问的编译器优化 在通话的点上。
但我没有得到这个,因为除了return
调用之外,没有其他内存访问;当然,编译器不会在下面移动作业吗?
有人可以澄清这个障碍的目的吗?
_Load_seq_cst_4
是inline
函数。编译器屏障在那里可以阻止重新排序使用以后的代码在调用函数中,该嵌入到
例如,考虑阅读seqlock。(与此实际实施相比过于简单(。
#include <atomic>
atomic<unsigned> sequence;
atomic_long value;
long seqlock_try_read() {
// this would normally be the body of a retry-loop;
unsigned seq1 = sequence;
long tmpval = value;
unsigned seq2 = sequence;
if (seq1 == seq2 && (seq1 & 1 == 0)
return tmpval;
else
// writer was modifying it, we should retry the loop
}
如果我们没有阻止编译时间重新排序,则编译器可以将sequence
的两种读取合并到一个访问中,例如也许像这样
long tmpval = value;
unsigned seq1 = sequence;
unsigned seq2 = sequence;
这将打败锁定机制(在修改数据之前,作者一次将sequence
递增,然后在完成数据后再次增加(。读者完全是无锁的,但这不是"无锁"算法
内的屏障每个 load
功能在内线后用其他事物重新排序。
(C 11内存模型非常弱,但是X86存储器模型很强,仅允许保存重新排序。通过以后的负载/商店阻止编译时间重新排序足以为您提供获得/顺序抗性负载在运行时。x86:这里需要内存屏障吗?(
btw,一个更好的示例可能是某些非atomic
变量在atomic
标志中看到某个值后读取/写入的东西。MSVC可能已经避免重新排序或合并原子访问,在SEQLOCK中,所保护数据也必须为atomic
。
为什么编译器不合并冗余std :: Atomic写入?