You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

std::atomic自交换是否为原子操作?两段模增代码原子性疑问

std::atomic相关原子性问题解答

1. 将std::atomic对象与自身进行exchange操作的原子性

std::atomic::exchange本身是原子操作,它会原子地把原子对象的值替换为传入的参数,并返回旧值。但要注意:如果exchange的参数依赖该原子对象的当前值,参数的计算过程是在exchange原子操作之外的,整个「计算参数+执行exchange」的组合逻辑并不是原子的。

2. 第一段代码是否原子执行?

代码:

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

这段代码不是原子执行,原因如下:

  • id.load()是原子操作,但它返回的只是当前线程读取到的id的快照值。
  • 后续的+1和% maxId是普通非原子计算,在这个过程中,其他线程完全可以修改id的值。
  • 最后exchange虽是原子操作,但它使用的是之前基于旧快照计算出的结果,会引发竞态条件:比如线程A读取id为2,线程B同时读取id为2并修改为3,线程A最终会把id设置为3((2+1)%maxId),直接覆盖线程B的修改,导致数据不一致。

3. 第二段代码是否具备原子性?

代码:

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

这段代码既不具备原子性,逻辑还存在错误:

  • ++id是std::atomic的原子自增操作,会原子地将id加1并返回新值,但执行完成后,% maxId是非原子计算,期间其他线程仍可修改id。
  • 更关键的是:++id已经修改了id的值,之后的exchange又会把id替换为(++id的结果)%maxId,相当于对id进行了两次修改,这两次操作之间存在间隙,完全可能被其他线程打断,同时这也不是你想要的「模增」逻辑(比如id原本是maxId-1,++id后变成maxId,exchange再改成0,但中间若有其他线程修改id,结果会彻底混乱)。

正确的原子模增实现方式

要实现原子的「模增」操作,推荐使用std::atomic::compare_exchange_weak(或compare_exchange_strong)的循环模式,它能保证「读取-计算-写入」的逻辑在无竞争时原子完成,有竞争时自动重试:

int current = id.load();
int desired;
do {
    desired = (current + 1) % maxId;
    // 尝试将id从current替换为desired,成功则退出循环,失败则更新current为当前id值并重试
} while (!id.compare_exchange_weak(current, desired));
// 此时current是替换前的旧值,desired是最终写入的新值

如果maxId是2的幂,也可以用fetch_add配合位运算实现,但这种方式仅适用于特定场景,通用场景还是优先选择compare_exchange的循环实现。


内容的提问来源于stack exchange,提问作者pajczur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 17:50:34