问题描述
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功能会发出一堆C28112警告("通过互锁函数访问的变量(Foo)必须始终通过互锁函数访问。")。
如果简单的阅读真的是正确的方式,我想我可以用这样的话让他们安静下来
#define atomic_load(object)
__pragma(warning(push))
__pragma(warning(disable:28112))
(*(object))
__pragma(warning(pop))
但是分析器坚持我应该始终使用Interlocked*
函数,这让我相信一定有更好的方法。如果是这样,那是什么呢?
推荐答案
我认为这里忽略分析器是可以接受的,因为the documentation表示简单地读取寄存器宽度变量是安全的(在32位系统上为32位,在64位系统上为64位)。The warning documentation itself基本上表示它过于谨慎,即使访问可能是安全的。
也就是说,如果您想让它闭嘴,您总是可以使用幂等运算来获得所需的行为。例如,您可以只定义:
#define atomic_load(object) InterlockedOr((object), 0)
由于按位或WITH0
永远不会更改值,并且它始终返回原始值,因此最终结果是读取原始值,同时自动不写入任何内容。
如果您使用memory_order_relaxed
模拟atomic_load_explicit
,则使用InterlockedOrNoFence
to avoid memory barriers可能会获得更好的性能,但对于模拟默认(顺序一致)atomic_load
,您需要坚持使用InterlockedOr
。
InterlockedOr
大多是任意选择的(理论上,它在硬件上可能比具有加法或减法之类进位的操作略快一些),但InterlockedXor
与其他几种操作的行为应该是相同的,只要它们是用它们的等价值完成的。
您也可以以类似的方式使用InterlockedCompareExchange
;需要进行测试以确定哪个更快:
#define atomic_load(object) InterlockedCompareExchange((object), 0, 0)
同样,如果值已经是0,则将其设置为零,但您真正使用它的唯一目的是获得返回值,即无操作交换之前的原始值。
这篇关于用MSVC实现C语言中的原子加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!