c++ - std::atomic 与自身交换时是原子操作吗?

标签 c++ atomic modulo stdatomic

下面的代码会自动执行吗?

const int oldId = id.exchange((id.load()+1) % maxId);

哪里idstd::atomic<int> ,和maxId是某个整数值。

我在 google 和 stackoverflow 上搜索了 std::atomic模增量。我找到了一些主题,但找不到如何正确执行此操作的明确答案。

就我而言,更好的是使用:

const int newId = id.exchange((++id) % maxId);

但我仍然不确定它是否会自动执行。

最佳答案

不,这不是原子的,因为 load()exchange() 是单独的操作,并且没有什么可以阻止 id加载之后、交换之前获取更新。在这种情况下,您的交换将写入一个基于过时输入计算出的值,因此您最终会错过更新。

您可以使用简单的compare_exchange循环实现模增量:

int val = id.load();
int newVal = (val + 1) % maxId;
while (!id.compare_exchange_weak(val, newVal) {
  newVal = (val + 1) % maxId;
}

如果compare_exchange失败,它会执行重新加载并使用更新后的值填充val。所以我们可以重新计算newVal并重试。

编辑:

比较交换循环的重点是处理加载和比较交换之间有人可能更改id的情况。这个想法是:

  1. 加载id的当前值
  2. 计算新值
  3. 当且仅当当前存储在 id 中的值与我们在 1 中读取的值相同时,用我们自己的值更新 id。如果是这种情况,我们就完成了,否则我们从 1 重新开始。

compare_exchange 允许我们在一个原子操作中执行比较和条件更新。 compare_exchange 的第一个参数是预期值(我们在比较中使用的值)。该值通过引用传递。因此,当比较失败时,compare_exchange会自动重新加载当前值并更新提供的变量(在我们的例子中为val)。

由于 Peter Cordes 正确地指出这可以在 do-while 循环中完成以避免代码重复,所以它是:

int val = id.load();
int newVal;
do {
  newVal = (val + 1) % maxId;
} while (!id.compare_exchange_weak(val, newVal);

关于c++ - std::atomic 与自身交换时是原子操作吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75447688/

相关文章:

java - 未使用 QAndroidJniObject 调用的三个函数之一

c++ - 计算存在的 shared_ptr 数

c++ - 友元函数未声明的标识符

c++ - 为什么存储到原子 unique_ptr 会导致崩溃?

multithreading - 锁(线程)是原子的吗?

javascript - 对非常大的数进行模运算

C# ModInverse 函数

c# - minHessian 的 OpenCV SURFDetector 范围

ruby - Redis 管道作为原子

C# 编译器奇怪的尝试将短结果转换为整数