egmkang 服务端开发工程师

Rust中的shared_ptr

2015-03-03

rust语言中内置了shared_ptr这种内存管理机制. 查阅文档可以知道, 内置了RcArc两个类. 其中Rc不是线程安全的, Arc是线程安全的.

下面以Arc为例来简单的看一下怎么用:(其实比较搞笑的是, Rc性能居然比Arc性能要差….)

let arc = Arc::new(UnsafeCell::new(10i32));
unsafe{ *arc.get() = 20i32; }
let arc2 = arc.clone();
assert!(unsafe{ *arc.get() }, unsafe{ *arc2.get() });

Arc就只有clone, downgrade等成员函数, 那个get是UnsafeCell的成员函数, *arc就会得到Arc模板参数的成员, 上面例子中就是UnsafeCell<i32>. 这个类差不多就是这么用的.

不过, 你肯定在想, 你这只是shared_ptr, 那weak_ptr呢? 那么, 标准库里面确实提供了Weak, 一共有俩版本, 分别是Rc和Arc的Weak实现. 以Arc为例:

use std::sync::{Arc, Weak};
 
fn main() {
  let mut arc = Arc::new(1);
  let weak = arc.downgrade();
  let mut arc2 = weak.upgrade().unwrap();
  {
    let v = arc.make_unique();
    *v = 10;
  }
  let v1 = *arc.make_unique();
  let v2 = *arc2.make_unique();
  println!("{}, {}", v1, v2);
}

如果你看到这里, 以为问题就完了, 那么你就大错特错了. 你猜猜上面程序运行的结果是什么? 你能猜到v1v2的值不一样么?你估计也是刚才发现Arc是不能直接修改里面的值,一旦修改了,就会发生COW…

然后你就必须得用上最上面的UnsafeCell或者RefCell. 这俩Cell的区别就是, RefCell在一个scope之内只能borrow一次, borrow第二次的时候就跪了; UnsafeCell跟他的名字一样, 得用unsafe关键字来括起来, 否则编译不过去, 不过好处就是速度比较快.

本来我以为这样问题就搞定了. 但是让我蛋疼的事情又发生了. 这种事情在rust身上经常发生….. Weak<UnsafeCell<T>>是不能upgrade到一个Arc<UnsafeCell<T>>的, 也就是说, 你如果想要用UnsafeCell的话, 就只能用Arc, 如果对象循环引用, 那么就呵呵了. 关于这个问题, 我给rust开发组发了一个bug, 得到的回复是, 这不是bug, 是估计设计成这样的…

那么好了, 如果你真的需要一个跟C++一样的shared_ptr, 不好意思, 你得自己造一个.


下一篇 逃离牛市

Comments