32 位环境下 64 位变量的原子增量

Atomic increment of 64 bit variable on 32 bit environment

的答案时出现了一些有趣的事情,现在我无法理解 Interlocked.Increment(ref long value) 如何在 32 位系统上工作。让我解释一下。

Native InterlockedIncrement64 现在在为 32 位环境编译时不可用,好的,这是有道理的,因为在 .NET 中你不能按要求对齐内存,它可能会从 中调用管理然后他们放弃了它。

在 .NET 中,我们可以调用 Interlocked.Increment() 并引用一个 64 位变量,我们仍然对其对齐没有任何限制(例如在一个结构中,我们也可以使用 FieldOffsetStructLayout) 但文档没有提到任何限制 (AFAIK)。很神奇,很管用!

Hans Passant 指出 Interlocked.Increment() 是 JIT 编译器识别的 特殊 方法,它将发出对 COMInterlocked::ExchangeAdd64() which will then call FastInterlockExchangeAddLong which is a macro for InterlockedExchangeAdd64 which shares same limitations of InterlockedIncrement64.

的调用

现在我很困惑。

忘记一秒钟托管环境并返回本机。为什么 InterlockedIncrement64 不能工作而 InterlockedExchangeAdd64 可以? InterlockedIncrement64 是一个宏,如果内部函数不可用并且 InterlockedExchangeAdd64 有效,那么它可以作为对 InterlockedExchangeAdd64...

的调用来实现

让我们回到托管:如何在 32 位系统上实现原子 64 位增量?我想句子 "This function is atomic with respect to calls to other interlocked functions" 很重要,但我仍然没有看到任何代码(感谢 Hans 指出更深入的实现)来做到这一点。当内部函数不可用时,让我们从 WinBase.h 中选择 InterlockedExchangedAdd64 实现:

FORCEINLINE
LONGLONG
InterlockedExchangeAdd64(
    _Inout_ LONGLONG volatile *Addend,
    _In_    LONGLONG Value
    )
{
    LONGLONG Old;

    do {
        Old = *Addend;
    } while (InterlockedCompareExchange64(Addend,
                                          Old + Value,
                                          Old) != Old);

    return Old;
}

reading/writing 怎么可能是原子的?

您必须继续追踪,InterlockedExchangeAdd64() 会将您带到 WinNt.h SDK 头文件。您会在哪里看到它的许多版本,具体取决于目标体系结构。

这通常会折叠为:

#define InterlockedExchangeAdd64 _InterlockedExchangeAdd64

它将责任转嫁给编译器内部函数,在 vc/include/intrin.h 中声明并由编译器的后端实现。

或者换句话说,不同版本的 CLR 会有不同的实现。多年来,x86、x64、Itanium、ARM、ARM8、PowerPC 从我的脑海中浮现出来,我肯定遗漏了一些在 Apple 使其变得无关紧要之前用于启动 WindowsCE 的东西。对于 x86,这最终由 LOCK CMPXCHNG8B 处理,LOCK CMPXCHNG8B 是一种专用处理器指令,可以处理未对齐的 64 位变量。我没有硬件来查看它在其他 32 位处理器上的样子。

请记住,托管代码的目标体系结构在编译时并未确定。抖动使 MSIL 在运行时适应目标。这与 C++/CLI 项目不太相关,因为如果您使用 /clr 而不是 /clr:pure 进行编译并且只有 x86 和 x64 可以工作,您通常必须选择一个目标。但是管道已经到位,所以宏不是很有用。