Rust中无Mutex实现多线程共享可变数据及未定义行为解析
高并发物理模拟中Rust多线程共享可变缓冲区的方案探讨
背景
我要开发高并发物理模拟程序,原本计划用C实现,为了利用高级特性转而选择Rust。按照Rust线程安全规范应采用消息传递等通信方式,但必须实现多线程共享可变缓冲区,同时需要对不同实现技术做基准测试。
已尝试的三种方案
方案一:Arc<Data> + transmute强制转换
- 采用
Arc<Data>共享不可变状态 - 需要修改数据时,用
transmute将&Data转换为&mut Data - 问题:即使在
unsafe代码块中也无法通过编译。原因是编译器会基于数据不可变的假设做优化(如缓存不更新),这类优化可通过Cell等内部可变性包装器规避,但本方案未做处理。
方案二:Arc<UnsafeCell<Data>>
- 用
Arc<UnsafeCell<Data>>封装共享数据 - 通过
data.get()获取裸指针访问数据 - 问题:无法编译,因为
UnsafeCell不具备Send特性。官方提供的替代方案是SyncUnsafeCell,但在Rust 1.66稳定版中仍处于不稳定状态,而程序需要在稳定版生产环境运行。
方案三:Arc<Mutex<Data>> + 提前获取裸指针
- 线程启动流程:锁定互斥锁,将获取到的
&mut Data强制转换为*mut Data,随后释放互斥锁 - 后续修改操作直接使用该
*mut指针 - 疑问:在不考虑数据竞争的前提下,该方案的安全性是否与
SyncUnsafeCell一致?
补充说明
并发修改的仅为f32类型的值,不存在并发内存分配或复杂操作,最坏情况仅为f32值错乱。
方案三的安全性分析(不考虑数据竞争)
从内存安全维度看,该方案与SyncUnsafeCell等价,理由如下:
- 线程启动时通过
Mutex锁定拿到的&mut Data是唯一可变引用,完全符合Rust借用规则,此时转换为*mut指针是安全的 Arc确保了Data的生命周期覆盖所有线程的运行周期,不会出现悬垂指针问题- 只要接受数据竞争导致的
f32值错乱(如补充说明中的最坏情况),该方案在稳定版Rust中是可行的,两者均仅放弃了Rust编译期的数据竞争检查,不会引入悬垂指针、野指针等额外内存不安全问题
内容的提问来源于stack exchange,提问作者uben
相关产品推荐
相关产品推荐

