问题描述
我一直在逐步处理在我使用64位项目在VS2017上分配给atomic_long
类型时涉及的函数调用.我特别想看看将atomic_long
复制到非原子变量中时,以及周围是否有锁定时会发生什么情况.
I have been stepping through the function calls that are involved when I assign to an atomic_long
type on VS2017 with a 64bit project. I specifically wanted to see what happens when I copy an atomic_long
into a none-atomic variable, and if there is any locking around it.
atomic_long ll = 10;
long t2 = ll;
最终它以该调用结束(我已经删除了ifdef
删除的一些代码)
Ultimately it ends up with this call (I've removed some code that was ifdef
ed out)
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位值的普通读取将是原子的:
Now, I've read from MSDN that a plain read of a 32bit value will be atomic:
...这解释了为什么没有Interlocked
函数只能阅读;仅用于更改/比较的那些.我想知道的是_Compiler_barrier()
位在做什么.这是#define
d作为
...which explains why there is no Interlocked
function for just reading; only those for changing/comparing. What I'd like to know is what the _Compiler_barrier()
bit is doing. This is #define
d as
__MACHINE(void _ReadWriteBarrier(void))
...并且我再次在 MSDN 这个
...and I've found on MSDN again that this
但是我不明白这一点,因为除了return
调用之外没有其他内存访问;当然,编译器不会将分配移到该位置以下吗?
But I don't get this, as there are no other memory accesses apart from the return
call; surely the compiler wouldn't move the assignment below that would it?
有人可以澄清这个障碍的目的吗?
Can someone please clarify the purpose of this barrier?
推荐答案
_Load_seq_cst_4
是inline
函数.编译器壁垒可以阻止在此内联的调用函数中使用以后的代码对代码进行重新排序.
_Load_seq_cst_4
is an inline
function. The compiler barrier is there to block reordering with later code in the calling function this inlines into.
例如,考虑阅读 SeqLock . (从此实际实现).
For example, consider reading a SeqLock. (Over-simplified from this actual implementation).
#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
的两次读取合并到单个访问中,就像这样
If we didn't block compile-time reordering, the compiler could merge both reads of sequence
into a single access, like perhaps like this
long tmpval = value;
unsigned seq1 = sequence;
unsigned seq2 = sequence;
这将使锁定机制失效(在这种情况下,编写器在修改数据之前先递增sequence
,然后在完成后再次递增).读取器完全是无锁的,但它不是无锁"算法,因为如果写入器在更新过程中被卡住,则读取器将无法读取任何内容.
This would defeat the locking mechanism (where the writer increments sequence
once before modifying the data, then again when it's done). Readers are entirely lockless, but it's not a "lock-free" algo because if the writer gets stuck mid-update, the readers can't read anything.
每个load
函数内的 障碍物在插入后阻止与其他事物重新排序.
The barrier within each load
function blocks reordering with other things after inlining.
(C ++ 11内存模型很弱,但是x86内存模型很强,只允许对StoreLoad进行重新排序.在以后的加载/存储中阻止编译时重新排序足以使您获得获取/顺序一致性负载 x86:这里是否需要内存屏障?)
(The C++11 memory model is very weak, but the x86 memory model is strong, only allowing StoreLoad reordering. Blocking compile-time reordering with later loads/stores is sufficient to give you an acquire / sequential-consistency load at runtime. x86: Are memory barriers needed here?)
顺便说一句,一个更好的例子可能是在atomic
标志中看到某个值之后读取/写入一些非atomic
变量的情况. MSVC可能已经避免了原子访问的重新排序或合并,并且在seqlock中,受保护的数据也必须为atomic
.
BTW, a better example might be something where some non-atomic
variables are read/written after seeing a certain value in an atomic
flag. MSVC probably already avoids reordering or merging of atomic accesses, and in the seqlock the data being protected also has to be atomic
.
为什么编译器不合并冗余的std :: atomic写? a>
Why don't compilers merge redundant std::atomic writes?
这篇关于_Compiler_barrier()在32位读取时的用途的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!