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

多线程无同步/原子操作访问uint64_t,是否会读到非预期范围值?

会出现吗?答案是:在特定场景下完全可能读到从未写入过的值,但你的0-100递增场景可能暂时不会暴露问题

先给你拆解清楚:

核心原因:CPU对64位内存操作的原子性支持差异

不是所有CPU架构都能一次性完成64位数据的读取或写入。比如32位ARM、32位x86这类架构,会把uint64_t的读写拆成两次32位的内存操作。这就给竞态条件留了口子——如果读线程刚好卡在两次32位操作之间读取,就会得到一个"拼凑"出来的值:一半是旧值,一半是新值。

你的0-100递增场景为什么没出问题?

因为0到100的数值都远小于2^32(4294967296),所以uint64_t的高32位始终是0,写入操作只会修改低32位。这时候:

  • 在32位架构上,写入只是单一的32位存储操作,读线程要么读到完整的旧值(低32位旧+高32位0),要么完整的新值(低32位新+高32位0),不会出现奇怪的中间值;
  • 在64位架构上,CPU原生支持64位原子读写,读线程只能读到旧值或新值。

所以你当前的测试场景下,确实很难观测到异常,但这只是巧合,不是必然安全。

什么时候会读到从未写入过的值?

当变量的值需要跨32位边界时,比如从4294967295(0xFFFFFFFF)递增到4294967296(0x100000000):

  • 在32位架构上,写入操作会拆成两步:先把低32位从0xFFFFFFFF改成0x00000000,再把高32位从0x00000000改成0x00000001;
  • 如果读线程在这两步之间读取,就会得到0x0000000000000000——这个值既不是旧值也不是新值,完全是从未写入过的"畸形值",而且远远超出了你预期的范围。

另外,即使是64位架构,如果编译器对写入操作做了奇怪的优化(比如把64位变量拆成两个32位变量处理),也可能出现类似问题,只是概率极低。

为什么你现在没看到异常?

大概率是这几个原因:

  1. 你用的是64位CPU/系统,原生支持64位原子操作;
  2. 测试的数值范围太小,没触及32位边界;
  3. 竞态条件是偶发的,需要成千上万次测试才可能触发一次。

要不要处理?必须要!

哪怕当前场景没问题,也绝对不能依赖这种"巧合":

  • 代码移植到32位架构就会立刻出问题;
  • 后续如果修改逻辑,让变量值超出低32位范围,会出现难以排查的偶发bug;
  • 编译器版本或优化选项变化,也可能打破当前的"安全"状态。

正确的做法是用原子操作来同步,比如C++里的std::atomic<uint64_t>,或者C语言里的_Atomic uint64_t,确保读写操作的原子性和内存可见性,彻底杜绝竞态条件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:17:06