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

tzdcorbm  于 2023-03-10  发布在  其他
关注(0)|答案(1)|浏览(204)

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

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

其中idstd::atomic<int>,而maxId是某个整数值。
我在google和stackoverflow上搜索了std::atomic模增量,我找到了一些主题,但是我找不到明确的答案,如何正确地做到这一点。
在我的情况下,更好的方法是用途:

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

但我仍然不确定它是否会被原子地执行。

4bbkushb

4bbkushb1#

不,这不是原子操作,因为load()exchange()是独立的操作,没有什么可以阻止idload之后、exchange之前更新,在这种情况下,exchange将写入一个基于过时输入计算的值,因此您最终会错过更新。
您可以使用一个简单的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的当前值
1.计算新值
1.用我们自己的值更新id * 当且仅当 * 当前存储在id中的值与我们在1中读取的值相同。如果是这种情况,我们就完成了,否则我们从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);

相关问题