使用 MSVC 在 C 中实现原子负载

nem*_*equ 7 c atomic interlocked visual-c++

TL;DR:我需要相当于 C11 的 Microsoft C(不是 C++)atomic_load。有谁知道正确的功能是什么?

我有一些使用原子的非常标准的代码。就像是

do {
  bar = atomic_load(&foo);
  baz = some_stuff(bar);
} while (!atomic_compare_exchange_weak(&foo, &bar, baz));
Run Code Online (Sandbox Code Playgroud)

我想弄清楚如何用 MSVC 处理它。CAS 很简单 ( InterlockedCompareExchange),但atomic_load事实证明更麻烦。

也许我遗漏了一些东西,但MSDN上的同步函数列表似乎没有任何简单加载的东西。我唯一能想到的就是这样的东西InterlockedOr(object, 0),它会为每个负载生成一个商店(更不用说围栏了)……

只要变量是可变的,我认为只读取值是安全的,但是如果我这样做,Visual Studio 的代码分析功能会发出一堆C28112警告(“通过互锁函数访问的变量 (foo) 必须始终可以通过互锁功能访问。”)。

如果简单的阅读真的是正确的方式,我想我可以用类似的东西让那些人沉默

#define atomic_load(object) \
  __pragma(warning(push)) \
  __pragma(warning(disable:28112)) \
  (*(object)) \
  __pragma(warning(pop))
Run Code Online (Sandbox Code Playgroud)

但是分析器坚持我应该始终使用这些Interlocked*函数,这让我相信一定有更好的方法。如果是这样,那是什么?

Sha*_*ger 5

我认为这里忽略分析器是可以接受的,因为文档说寄存器宽度变量的简单读取是安全的(32 位系统上的 32 位,64 位系统上的 64 位)。警告文档本身基本上说它过于谨慎,即使访问可能是安全的。

也就是说,如果你想关闭它,你总是可以使用幂等Interlocked操作来获得所需的行为。例如,您可以定义:

#define atomic_load(object) InterlockedOr((object), 0)
Run Code Online (Sandbox Code Playgroud)

由于按位或 with0永远不会更改该值,并且它始终返回原始值,因此最终结果是读取原始值,同时原子地不写入任何内容。

如果你是模拟atomic_load_explicitmemory_order_relaxed您可以通过使用获得更好的性能InterlockedOrNoFence,以避免内存的障碍,但对于模拟的默认(顺序一致)atomic_load你想坚持InterlockedOr

InterlockedOr大部分是任意选择的(理论上它在硬件上可能比加法或减法之类的进位运算略快),但是InterlockedXor0 的行为应该与其他几个操作相同,只要它们是用他们的身份价值。

您也可以InterlockedCompareExchange以类似的方式使用;需要测试来确定哪个更快:

#define atomic_load(object) InterlockedCompareExchange((object), 0, 0)
Run Code Online (Sandbox Code Playgroud)

同样,如果该值已经为 0,则将其设置为零,但您真正使用它的目的只是获取返回值,即无操作交换之前的原始值。