Rust多线程开发中能否无锁使用Arc并修改其内部值?
问题结论
你写的这段无锁直接修改Arc内部值的代码完全不合法,连编译阶段都无法通过,不存在可运行性。
核心原因说明
Arc<T>的作用仅为实现原子引用计数的跨线程共享所有权,它只保证引用计数的增减操作是线程安全的,不负责内部数据的并发可变访问安全。Arc<T>默认对外暴露的是内部值的不可变共享引用&T,你直接写*x += 1尝试修改内部值,首先就违反了Rust的基本借用规则,编译器会直接抛出cannot assign to data in anArc``的错误。- 「不关心读取顺序」不能作为绕过并发安全规则的理由:Rust的安全抽象会在编译期彻底禁止数据竞争(Data Race),数据竞争属于未定义行为,哪怕你业务逻辑上能接受读到旧值/新值的任意结果,编译器也不会允许安全代码中出现这类风险。不要尝试用
unsafe块绕过编译检查直接修改普通Arc包裹的内部值,这类写法会触发数据竞争,可能出现值错乱、程序崩溃等完全不可预期的运行问题。 Arc本身从来不是用来替代锁或者原子类型的:如果你确实需要跨线程共享可变数据,要根据场景选择搭配的类型:- 简单数值、布尔值这类基础类型的无锁并发场景,直接用
Arc<AtomicI32>/Arc<AtomicBool>等标准库原子类型即可,这类类型本身实现了无锁的线程安全读写,不需要额外加互斥锁 - 复杂自定义类型的并发可变场景,使用
Arc<Mutex<T>>/Arc<RwLock<T>>搭配锁实现安全访问,不要尝试直接修改Arc包裹的普通值。
- 简单数值、布尔值这类基础类型的无锁并发场景,直接用
修正示例
如果你要实现代码里跨线程计数、子线程读值的逻辑,符合你「不关心读写顺序」需求的无锁写法可以参考如下实现:
use std::{ sync::{ atomic::{AtomicI32, Ordering}, Arc, }, thread, }; fn main() { // 用Arc包裹原子类型实现无锁共享可变访问 let x = Arc::new(AtomicI32::new(0)); let y = Arc::clone(&x); thread::spawn(move || { for i in 0..10 { // 用Relaxed内存序不强制读写顺序,符合你的需求,性能开销极低 let val = y.load(Ordering::Relaxed); println!("exe:{},{}", i, val); } }); loop { let cur = x.load(Ordering::Relaxed); if cur >= 100000 { break; } // 写操作同样为原子操作,无锁 x.store(cur + 1, Ordering::Relaxed); } }
内容的提问来源于stack exchange,提问作者yansainity
相关产品推荐
相关产品推荐

