如何在没有内存屏障的情况下实现 InterlockedIncrement

Posted

技术标签:

【中文标题】如何在没有内存屏障的情况下实现 InterlockedIncrement【英文标题】:How can i implement a InterlockedIncrement without a memory barrier 【发布时间】:2012-10-15 03:08:11 【问题描述】:

我实现了一个引用计数模式,为此我需要互锁设施,但没有内存栅栏(据我所知)。

不幸的是,只有 Windows 具有 InterlockedDecrementNoFence 编译器内在函数。 我如何使用 asm 内联来做到这一点?我也需要这个用于 gcc/clang。

【问题讨论】:

嗯...无论如何,MSVC 和 gcc 中的内联汇编会有所不同...而且屏障最好放在它所属的地方。 【参考方案1】:

至少缺少一个内在的内存栅栏操作(取决于平台)对于这个操作的语义来说几乎是不可能的。为了使原子递增/递减工作,其他处理器必须保证在对变量执行自己的操作之前看到原子操作的值,并且对变量的操作的可见性必须有一定的保证总数命令。内存屏障,即使是像 x86 这样标准内存操作具有获取/释放语义的强内存模型中的隐式屏障,对于原子递增/递减操作的正确性也是至关重要的。

请记住,即使 MSVC/gcc 的文档说存在内存屏障,由于强内存模型,在 x86 上也不会应用实际的内存屏障(即 MFENCE 指令)平台,而是内存总线上的一个锁,以确保操作的原子性。另一方面,IA64 需要内存屏障,因为该平台的内存模型较弱。 ARM 也是如此。

【讨论】:

所以我确实误解了障碍的概念。因为我认为它只会影响挂起的不同读/写操作的顺序。使用引用计数完全没有排序问题,因为只涉及一个内存位置。在多处理器系统的情况下,缓存一致性协议已经解决了这个问题。 请记住,我说的是“内在”屏障......根据平台的不同,可能没有显式的内存屏障操作,但您仍然需要支付惩罚,因为您必须这样做使原子操作的结果对所有其他处理器可见,并且每个原子操作必须在给定的内存地址上具有总顺序。【参考方案2】:

这不是直接答案,而是替代建议。如果可以使用 C11(或 C++11),那么使用 memory_order_relaxed 的原子操作怎么样?您的编译器可能在弱内存模型平台上不会生成内存栅栏。 (这取决于编译器供应商/版本)

#include <stdatomic.h>

atomic_int var;
int oldval;

oldval = atomic_fetch_sub_explicit(&var, 1, memory_order_relaxed);

【讨论】:

以上是关于如何在没有内存屏障的情况下实现 InterlockedIncrement的主要内容,如果未能解决你的问题,请参考以下文章

内存屏障是如何工作的?

解密内存屏障

解密内存屏障

共享内存中进程间的屏障实现

如何区分interlock和jersey

内存屏障