写点什么

Rust RefCell 多线程读为什么也 panic 了?

  • 2025-11-15
    北京
  • 本文字数:861 字

    阅读完需:约 3 分钟

这是最近实战中遇到的一个小知识点,没理解之前觉得「不可能」,反应过来之后,觉得自己很蠢🤣,借本文记录下。


看一段复现代码:


struct MyRefCell<T>(RefCell<T>);
unsafe impl<T> Sync for MyRefCell<T> {}
fn main() { let shared = Arc::new(MyRefCell(RefCell::new(0usize)));
let mut handles = Vec::new(); for i in 0..100 { let s = shared.clone(); handles.push(thread::spawn(move || { thread::sleep(Duration::from_millis(10 * (i % 3) as u64)); let r = s.0.borrow(); let r = s.0.borrow_mut(); println!("thread {} read {}", i, *r); })); }
for h in handles { let _ = h.join(); }
println!("done");}
复制代码



​多线程读一个 RefCell 封装的变量,却发生了 panic,原因是:​​**already mutably borrowed: BorrowError**


即 RefCell 修饰的变量在 borrow 时检测到已经 borrow_mut 了,但是代码里其实没有 borrow_mut 的地方,就很神奇。


另一个迷惑的地方是,多线程读变量居然也是不安全的,也会 panic。


或许有小伙伴不理解 RefCell,这里简单介绍下:


Rust 的借用检查一般在编译期,即一个可变借用(&mut T)同时只能存在一个,不可变借用(&T)和可变借用不能共存;但在实际场景中,借用关系往往很难在编译期满足,这时候就可以用 RefCell,RefCell 提供两个操作符:borrow()borrow_mut(),支持在​运行时检查借用关系​,如果运行时违法借用规则,会 panic。


在我们的代码中,其实​没有违反借用规则​,因为我们只有不可变借用,但还是 panic 了,为什么呢?


原因在于 RefCell borrow() 的​底层实现不是原子的​,看着是多线程读,其实内部存在写操作,变成了​隐藏的多线程写​,如下:




可以看出borrow()borrow_mut()内部实现存在写操作,多线程访问时,flag 状态管理可能出错,导致 panic。


同样的问题,在 Swift 中,如果是多线程读一个变量,是安全的吗?


答案我们将在公众号「非专业程序员 Ping」的下一期文章揭晓,欢迎订阅交流!

发布于: 1 小时前阅读数: 7
用户头像

还未添加个人签名 2019-03-12 加入

还未添加个人简介

评论

发布
暂无评论
Rust RefCell 多线程读为什么也panic了?_rust_非专业程序员Ping_InfoQ写作社区