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 位变量,我们仍然对其对齐没有任何限制(例如在一个结构中,我们也可以使用 FieldOffset
和 StructLayout
) 但文档没有提到任何限制 (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 可以工作,您通常必须选择一个目标。但是管道已经到位,所以宏不是很有用。
写 Interlocked.Increment(ref long value)
如何在 32 位系统上工作。让我解释一下。
Native InterlockedIncrement64
现在在为 32 位环境编译时不可用,好的,这是有道理的,因为在 .NET 中你不能按要求对齐内存,它可能会从 中调用管理然后他们放弃了它。
在 .NET 中,我们可以调用 Interlocked.Increment()
并引用一个 64 位变量,我们仍然对其对齐没有任何限制(例如在一个结构中,我们也可以使用 FieldOffset
和 StructLayout
) 但文档没有提到任何限制 (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 可以工作,您通常必须选择一个目标。但是管道已经到位,所以宏不是很有用。