英文:
Is it atomic operation when exchange std::atomic with itself?
问题
以下是您要翻译的内容:
"Will following code be executed atomically?
const int oldId = id.exchange((id.load()+1) % maxId);
Where id
is std::atomic<int>
, and maxId
is some integer value.
I searched google and stackoverflow for std::atomic
modulo increment. And I found some topics but I can't find clear answer how to do that properly.
In my case even better would be to use:
const int newId = id.exchange((++id) % maxId);
But I am still not sure if it will be executed atomically."
英文:
Will following code be executed atomically?
const int oldId = id.exchange((id.load()+1) % maxId);
Where id
is std::atomic<int>
, and maxId
is some integer value.
I searched google and stackoverflow for std::atomic
modulo increment. And I found some topics but I can't find clear answer how to do that properly.
In my case even better would be to use:
const int newId = id.exchange((++id) % maxId);
But I am still not sure if it will be executed atomically.
答案1
得分: 3
这不是原子操作,因为load()
和exchange()
是分开的操作,没有阻止id
在load
之后但在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
并重试。
编辑:
compare-exchange
循环的整个目的是处理在load
和compare-exchange
之间可能有人更改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));
英文:
No, this is not atomic, because the load()
and the exchange()
are separate operations, and nothing is preventing id
from getting updated after the load
, but before the exchange
. In that case your exchange
would write a value that has been calculated based on a stale input, so you end up with a missed update.
You can implement a modulo increment using a simple compare_exchange loop:
int val = id.load();
int newVal = (val + 1) % maxId;
while (!id.compare_exchange_weak(val, newVal) {
newVal = (val + 1) % maxId;
}
If the compare_exchange fails it performs a reload and populates val
with the updated value. So we can re-calculate newVal
and try again.
Edit:
The whole point of the compare-exchange-loop is to handle the case that between the load and the compare-exchange somebody might change id
. The idea is to:
- load the current value of
id
- calculate the new value
- update
id
with our own value if and only if the value currently stored inid
is the same one as we read in 1. If this is the case we are done, otherwise we restart at 1.
compare_exchange is allows us to perform the comparison and the conditional update in one atomic operation. The first argument to compare_exchange
is the expected value (the one we use in our comparison). This value is passed by reference. So when the comparison fails, compare_exchange
automatically reloads the current value and updates the provided variable (in our case val
).
And since Peter Cordes pointed out correctly that this can be done in a do-while loop to avoid the code duplication, here it is:
int val = id.load();
int newVal;
do {
newVal = (val + 1) % maxId;
} while (!id.compare_exchange_weak(val, newVal);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论