我有一对unsigned int32
std::atomic<u32> _start;
std::atomic<u32> _end;
有时我想用比较交换设置开始或结束,所以我不希望在整个64位对上使用CAS可能导致虚假失败。我只想使用32位CAS。
_end.compare_exchange_strong(old_end, new_end);
现在我可以将start和end作为一个原子64位读取。或两个独立的32位读取。进行一次64位原子获取(编译器添加适当的内存栅栏)而不是两个独立的32位原子位读取和两个内存栅栏(或者编译器会优化它吗?)
如果是这样,我将如何在c ++ 11中做到这一点?
答案 0 :(得分:3)
该标准不保证std::atomics
与基础类型具有相同的大小,也不保证atomic
上的操作是无锁的(尽管它们可能适用于uint32
最小)。因此,我很确定没有任何符合标准的方法将它们组合成一个64bit
原子操作。因此,您需要决定是否要将两个变量手动组合成64位变量(并且只能使用64位操作)。
作为示例,平台可能不支持64bit
CAS(对于第一个Pentium IIRC添加的x86,因此在编译486兼容时不可用。在这种情况下,它需要以某种方式锁定,所以原子可能包含64bit
变量和锁定。
关于围栏:这取决于您为操作指定的memory_order
。如果内存顺序指定两个操作需要按照它们被执行的顺序可见,编译器显然将无法优化栅栏,否则可能。当然假设你只针对x86 memory_order_seq_cst
实际上会从我记忆中发出一个屏障指令,所以更少的东西会阻碍编译器完成指令重新排序,但不会有实际的惩罚。)
当然,根据您的平台,您可以通过std::atomic<int32>
或int64
将两个union
作为reinterpret_cast
之一进行投射,从而获得豁免,标准不需要此行为,并且(至少在理论上)可以随时停止工作(新的编译器版本,不同的优化设置,......)
答案 1 :(得分:0)
如果您的两个整数需要原子更新,那么您必须将它们视为单个原子64位值,您真的没有其他选择。单独的整数更新不是原子的,也不可行。我同意Unions在这里并不相关,并建议你只需将这对整数转换为(INT64)并执行你的Cas64。
使用关键部分是过度杀伤 - 使用Cas64,它们只需要大约33个机器周期(未选择),而关键部分的成本更像是100个未安装的周期。
请注意,在版本化指针上执行此完全相同的操作是很常见的,32位指针由32位指针和32位指针组成,它们一起更新为一个,如上所述使用Cas64。此外,它们实际上必须“正确排列”,因为您从不希望这些值超出缓存行边界。