本文介绍了用MSVC实现C语言中的原子加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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语言中的原子加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-12 12:24