在回答另一个问题时,一些有趣的事情浮出水面,现在我不明白Interlocked.Increment(ref long value)
在32位系统上是如何工作的。让我解释一下。
当编译为32位环境时,本地InterlockedIncrement64
现在不可用,这是可以理解的,因为在.NET中无法按照要求对齐内存,它可能被从托管调用,所以他们放弃了它。
在.NET中,我们可以使用对64位变量的引用调用Interlocked.Increment()
,我们仍然没有任何关于其对齐方式的约束(例如在结构中,也可以使用FieldOffset
和StructLayout
),但文档没有提到任何限制(据我所知)。这是一种神奇的方法,它起作用了!
Interlocked.Increment()
是JIT编译器识别的一种特殊方法,它会发出对COMInterlocked::ExchangeAdd64()的调用,然后调用FastInterlockExchangeAddLong,这是InterlockedExchangeAdd64的一个宏,它与InterlockedIncrement64具有相同的限制。现在我很困惑。
请先忘记托管环境并回到本机环境。为什么
InterlockedIncrement64
无法工作,但InterlockedExchangeAdd64
可以?InterlockedIncrement64
是一个宏,如果内部函数不可用且InterlockedExchangeAdd64
起作用,则可能会将其实现为对InterlockedExchangeAdd64
的调用...让我们回到托管环境:在32位系统上如何实现原子64位增量?我想这句话“此函数与调用其他互锁函数相关”很重要,但我仍然没有看到任何代码(感谢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;
}
如何使其在读取/写入时是原子的?
InterlockedIncrement64
不能工作,但InterlockedExchangeAdd64
可以"? 你原来的回答是正确的,因为托管代码不能直接调用本地Win32 API并期望一切正常。它们都不会起作用。你必须使用托管助手。现在,托管助手的实现是本地代码,因此它调用本地函数。由于宏和内置函数在编译时解析,所以CLR的位数很重要。 - Cody GrayInterlockedExchangeAdd64
的文档提供了一些线索,它说:“此函数生成完整的内存屏障(或栅栏),以确保内存操作按顺序完成。”请注意,您上面展示的实现调用了InterlockedCompareExchange64
。在 32 位构建中,这会发出带有LOCK
前缀的CMPXCHG8B
指令。这确保了指令的原子执行。您永远不会获得一个锁定的读取而没有锁定的写入,因此写入目标是原子的。 - Cody Gray