下面的代码会自动执行吗?
const int oldId = id.exchange((id.load()+1) % maxId);
哪里id
是 std::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
的情况。这个想法是:
- 加载
id
的当前值 - 计算新值
- 当且仅当当前存储在
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/