使用 MSVC 在 C 中进行原子加载

Atomic load in C with MSVC

TL;DR:我需要与 C11 atomic_load 等效的 Microsoft C(而非 C++)。有人知道正确的功能是什么吗?

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

do {
  bar = atomic_load(&foo);
  baz = some_stuff(bar);
} while (!atomic_compare_exchange_weak(&foo, &bar, baz));

我正在尝试找出如何使用 MSVC 处理它。 CAS 很简单 (InterlockedCompareExchange),但 atomic_load 证明比较麻烦。

也许我遗漏了什么,但是 Synchronization Functions list on MSDN 似乎没有任何东西可以简单加载。我唯一能想到的就是像 InterlockedOr(object, 0) 这样的东西,它会为每个负载生成一个存储(更不用说栅栏了)……

只要变量是可变的,我认为只读取值是安全的,但如果我这样做,Visual Studio 的 code analysis feature emits a bunch of C28112 警告 ("A variable (foo) which is accessed via an Interlocked function must always be accessed via an Interlocked function.")。

如果简单的阅读真的是正确的方法,我想我可以用

这样的东西让那些人闭嘴
#define atomic_load(object) \
  __pragma(warning(push)) \
  __pragma(warning(disable:28112)) \
  (*(object)) \
  __pragma(warning(pop))

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

我认为在这里忽略分析器是可以接受的,鉴于 the documentation says simple reads of register width variables are safe (32 bit on 32 bit systems, 64 bit on 64 bit systems). The warning documentation itself 基本上表示它过于谨慎,即使访问可能是安全的。

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

#define atomic_load(object) InterlockedOr((object), 0)

由于按位或使用 0 永远不会更改值,并且它总是 returned 原始值,最终结果是读取原始值而原子地不写入任何内容。

如果您使用 memory_order_relaxed 模拟 atomic_load_explicit,则使用 InterlockedOrNoFence to avoid memory barriers 可能会获得更好的性能,但是为了模拟默认值(顺序一致)atomic_load,您需要想坚持 InterlockedOr.

InterlockedOr 主要是任意选择的(理论上它在硬件上可能比带进位的加法或减法运算稍快),但是带 0 的 InterlockedXor 应该表现相同方式,就像其他几个操作一样,只要它们完成了它们的标识值。

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

#define atomic_load(object) InterlockedCompareExchange((object), 0, 0)

再次,如果该值已经为 0,则将其设置为零,但您真正使用它的目的只是获取 return 值,即空操作交换之前的原始值.