file:///Users/fliter/rust-in-databend/17-%E6%8E%A2%E8%AE%A8Rust%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88/%E6%8E%A2%E8%AE%A8Rust%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88.pdf
file:///Users/fliter/rust-in-databend/19-%E6%8E%A2%E8%AE%A8Rust%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88II/%E6%8E%A2%E8%AE%A8Rust%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%202.pdf
Box、Vec | String、Cell | RefCell、Rc | Arc、RwLock | Mutex 都是什么?有何作用
这些Rust中的常见智能指针和同步原语都有以下作用:
Box
: 将数据放在heap中,实现类似C++指针的功能。 Vec
: 动态数组,内部使用Box将每个元素放入heap。 String: 可增长的UTF-8字符串,内部使用Vec存储字符数据。
Cell
: 线程安全的紧耦合内存类型,可用于在线程间传递值。 RefCell
: 运行时检查借用规则的智能指针,可在单线程下解决borrows问题。 Rc
: 引用计数指针,简化cloned值的分享工作。 Arc
: 可在线程间共享的Rc智能指针。 RwLock
: 读写锁,可实现多线程下只读共享、写独占的安全访问数据結构模式。 Mutex
: 互斥锁,保证对共享数据的独占访问。
它们主要用于解决Rust特有的内存管理问题,比如在堆上分配数据、实现共享数据访问、打破借用规则等。
为线程安全程序和多线程同步提供支持。
能给我举几个使用这些智能指针和同步原语的例子吗?
这里举几个使用Rust智能指针和同步原语的简单例子:
- 使用Box将结构体放在堆上:
1 | let b = Box::new(MyStruct { foo: "hello" }); |
- 使用Rc
实现引用计数:
1 | let r = Rc::new(String::from("hi")); |
- 使用Mutex
进行互斥操作:
1 | let mutex = Mutex::new(vec![1,2,3]); |
- 使用Cell
进行线程安全访问:
1 | let data = Cell::new(5); |
- 使用RefCell
进行借用检查:
1 | let r = RefCell::new(vec![1,2,3]); |
- 使用Arc
和RwLock 进行多线程访问:
1 | let data = Arc::new(RwLock::new(vec![1,2,3])); |
这些例子阐明了它们各自的用途。
能解释一下RefCell的作用是什么吗?
RefCell
运行时借用检查:RefCell
能在运行时检查和执行借用规则,而不是编译时检查。 宏观共享:允许在不需要线程安全的单线程情况下,宏观上共享数据而不考虑微观借用规则。
打破借用限制:RefCell
允许发生 ABA 问题,比如允许同时存在不可变和可变引用,这在编译时是不允许的。 实现 Interior Mutability:通过 RefCell 将内部可变性从外部暴露为不可变引用,达到通过不可变借用外部访问可变内部数据的目的。
编译时不安全为运行时安全:编译时无法检测的视情况,通过运行时检查避免发生错误。
补充 Mutex:比 Mutex 更轻量,用于暴露内部可变性而不是全局互斥锁。
总之,RefCell
RefCell 是否线程安全?
RefCell
具体来说:
RefCell
内部数据的读写访问是非线程安全的,可能会发生数据竞争导致错误。 它无法同步多个线程对内部数据的访问,一个线程在修改期间,其他线程可能获得了无效的引用。
即使在单线程下使用 RefCell
,发生死锁也无法检测和预防。 通过 RefCell
获取的引用在不同线程中使用,也可能违反其内部的借用规则。
所以为了使 RefCell
使用 Mutex<RefCell
> 实现互斥访问保证安全。 使用 RwLock<RefCell
> 实现可读可写模式的线程安全访问。 使用 Arc<RefCell
> 在线程间共享不可变引用。
所以总体来说,RefCell
RefCell 和 Mutex 有什么区别?
RefCell
适用场景:
RefCell
主要用于程序内部打破编译时借用规则,实现内部可变性。 Mutex 用于多线程环境下的互斥访问,提供全局锁保证同步。
性能:
RefCell
采用运行时检查,性能开销小于 Mutex。 Mutex 内部使用原子操作进行锁操作,运行开销较大。
访问模式:
RefCell
允许同时存在可变和不可变引用。 Mutex 一次只允许一个可变引用,其他线程阻塞等待。
线程安全性:
RefCell
本身不提供线程安全保证,需要额外采用同步原语。 Mutex 直接提供互斥访问的线程安全保证。
用于场景:
RefCell
用于内部可变性、管理内部引用计数等。 Mutex 一般用于实现参考计数、同步容器访问等全局互斥。
总的来说:
RefCell
RefCell 在什么情况下会发生 ABA 问题?
RefCell
- 当其他线程在 RefCell
上执行借用的过程中,内部数据发生了修改。
例如:
1 | thread1 通过 borrow() 获得不可变引用 A; |
这种情况下引用 A 看起来还有效,但实际上数据已经变了,违反了借用的目的。
- 当一个线程同时获得不可变和可变引用时,另一个线程再次修改数据。
例如:
1 | thread1 通过 borrow() 获得不可变引用 A; |
此时 A 和 B 的有效性已无法保证。
- 使用 Cell/RefCell 去实现类似 Atomic 数据结构时,可能会面临 ABA 问题。
所以总体来说,只要在 RefCell
2024.07.09
https://fenix0.com/smart-pointer/
引用和智能指针一个重要区别是,引用借用数据,智能指针大多情况下拥有数据。
从官方定义来看,智能指针重要特征是实现了Deref和Drop。
解引用deref(
*)是引用(&)的反操作。如果我们对一个引用 使用*操作符,将读到地址指向的内存 (*操作符和deref还是有一点点区别的)Drop trait 类似结构体生命周期结束的回调
Box 将数据保存在堆上,是最常见的智能指针
原文链接: https://dashen.tech/2017/03/30/Rust中的智能指针/
版权声明: 转载请注明出处.