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

多线程场景下Atomic包装类型与原生类型的差异问询

多线程场景下Atomic类型与原生类型的可见性差异

1. 其他线程能否看到非原子类型的修改?

先看你给出的非原子类型示例代码:

fn main() {
    let mut counter = 0;

    std::thread::scope(|scope| {
        scope.spawn(|| counter += 1)
    });

    println!("{counter}");
}

这段代码属于未定义行为,而且完全无法保证主线程能读到子线程修改后的1值。原因在于:

  • 非原子类型的读写没有同步机制,处理器会为每个线程缓存数据,子线程对counter的修改可能只停留在自身缓存,未同步到主内存;
  • 即使修改同步到主内存,主线程也可能继续使用自身缓存里的旧值;
  • 编译器还可能因无法感知线程间同步,对代码做重排序或优化,比如直接将初始值0保留在寄存器中,完全不读取主内存。

只有原子类型配合合适的内存顺序,才能保证线程间的可见性。比如你给出的原子类型示例:

fn main() {
    let counter = AtomicI32::new(0);

    std::thread::scope(|scope| {
        scope.spawn(|| counter.store(1, Ordering::Release))
    });

    println!("{}", counter.load(Ordering::Acquire)); // Ordering::Acquire to prevent from reordering previous instructions
}

这里的Release/Acquire内存顺序配对,能确保:

  • 子线程中store操作之前的所有写操作,在主线程执行load后都可见;
  • store的1值一定会被主线程读到——Release会强制将修改同步到主内存,Acquire会让主线程从主内存读取最新值,同时禁止指令重排序破坏可见性。

2. Ordering类型是否会影响store操作的值对其他线程的可见时机?

看你给出的Relaxed内存顺序示例:

fn main() {
    let counter = AtomicI32::new(0);

    std::thread::scope(|scope| {
        scope.spawn(|| counter.store(1, Ordering::Relaxed))
    });

    println!("{}", counter.load(Ordering::Relaxed));
}

首先明确:Ordering::Relaxed只保证原子操作的完整性(不会读到半写的中间值),不保证可见性的即时性。

具体到这个示例,无法确保一定会输出1。因为Relaxed没有内存屏障,子线程的store操作可能被处理器延迟写入主内存,主线程的load可能仍读取旧值。虽然原子类型保证修改最终会同步到主内存(不会像非原子类型那样永远不可见),但没有任何机制保证主线程执行load时能看到这个修改。

如果需要确保其他线程能及时看到修改,必须使用更强的内存顺序,比如Release/Acquire配对,或者Ordering::SeqCst(顺序一致性,最严格的内存顺序)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 00:41:12