C11 混合对变量的原子和非原子访问

C11 mixing atomic and non-atomic access to variable

有时您可能希望以原子方式和非原子方式访问一个变量。这就是为什么我觉得在 gcc 上你可以写类似这样的东西很方便:

int var = 0;
var++;
atomic_fetch_add(&var, 1);

但是这不能用 clang 4.0.1 编译:

error: address argument to atomic operation must be a pointer to _Atomic type ('int *' invalid)
atomic_fetch_add(&var, 1);

我能找到的最佳解决方案是强制转换:

int var = 0;
(*(int*)&var)++;
atomic_fetch_add(&var, 1);

有没有更简单、更便携的方法来实现这一点?

C11 中有两个接口允许您对限制较少的原子对象进行操作。

首先,您总是可以覆盖一个原子对象,当您知道您是唯一访问它的人时,通常在初始化阶段,为此使用 atomic_init

其次,如果在执行期间需要较少的访问保证,即使有多个线程,您可以使用限制较少的访问模式。即你可以做 atomic_fetch_and_add_explicit(&var, 1, memory_order_relaxed)。这仍然保证您的访问是 indivisible(您希望从原子中获得的属性之一)但它不能保证另一个线程何时看到更新的值。

但一般来说,如果原子访问对性能至关重要,那你就做错了。因此,在您尝试处理语义上困难的原子处理之前,请对您的代码进行基准测试,看看这是否真的是一个瓶颈。如果是这样,请首先考虑一种更改算法的方法,例如,通过在不受竞争影响的局部变量中进​​行更多计算。仅当所有这些都无法为您提供所需的性能时,请查看 C11 提供的不同内存语义。

C 标准定义的抽象机对存储的看法与大多数真实机器截然不同。特别是,它不是将内存访问视为可以根据所需情况以各种不同方式执行的操作,而是认为每个对象都支持一种读取和最多一种写入(const -限定对象不支持任何类型的写入);访问对象所需的读写类型取决于其类型。

这种方法可能对某些类型的硬件平台或某些优化策略有用,但严重不适用于现实世界平台上的许多类型的程序运行。不幸的是,该标准不承认程序员可以指示某些对象在大多数情况下应被视为 "ordinary" 存储的任何实用方法,但认识到它们在程序执行期间的某些特定时间需要更精确的内存语义.