11

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

在为 32 位环境编译时, NativeInterlockedIncrement64现在不可用,好吧,这是有道理的,因为在 .NET 中,您无法根据需要对齐内存,并且可能会从托管调用它,然后他们将其删除。

在 .NET 中,我们可以Interlocked.Increment()使用对 64 位变量的引用进行调用,我们仍然没有对其对齐的任何约束(例如在结构中,我们也可以使用FieldOffsetand StructLayout)但是文档没有提到任何限制(AFAIK )。这很神奇,它有效!

Hans Passant 指出,这Interlocked.Increment()是 JIT 编译器识别的一种特殊方法,它将发出对 COMInterlocked::ExchangeAdd64()的调用,然后调用FastInterlockExchangeAddLong ,这是InterlockedExchangeAdd64的宏,与InterlockedIncrement64具有相同的限制

现在我很困惑。

忘记一秒钟的托管环境,回到原生环境。为什么InterlockedIncrement64不能工作,但InterlockedExchangeAdd64可以?InterlockedIncrement64是一个宏,如果内在函数不可用并且InterlockedExchangeAdd64可以工作,那么它可以作为对InterlockedExchangeAdd64...的调用来实现

让我们回到托管:如何在 32 位系统上实现原子 64 位增量?我想句子“这个函数相对于对其他联锁函数的调用是原子的”很重要,但我仍然没有看到任何代码(感谢 Hans 指出更深层次的实现)来做到这一点。InterlockedExchangedAdd64当内在函数不可用时,让我们从 WinBase.h 中选择实现:

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

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

    return Old;
}

读/写怎么能是原子的?

4

1 回答 1

9

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

这通常会崩溃为:

#define InterlockedExchangeAdd64 _InterlockedExchangeAdd64

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

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

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

于 2016-06-01T07:42:17.143 回答