基础代码

既然我们已经确定了 Arc 实现的布局,让我们创建一些基本代码。

构造 Arc

我们首先需要一种构造 Arc<T> 的方法。

这很简单,因为我们只需要装箱 ArcInner<T> 并获取指向它的 NonNull<T> 指针。

impl<T> Arc<T> {
    pub fn new(data: T) -> Arc<T> {
        // We start the reference count at 1, as that first reference is the
        // current pointer.
        let boxed = Box::new(ArcInner {
            rc: AtomicUsize::new(1),
            data,
        });
        Arc {
            // It is okay to call `.unwrap()` here as we get a pointer from
            // `Box::into_raw` which is guaranteed to not be null.
            ptr: NonNull::new(Box::into_raw(boxed)).unwrap(),
            phantom: PhantomData,
        }
    }
}

Send 和 Sync

由于我们正在构建一个并发原语,我们需要能够在线程之间发送它。 因此,我们可以实现 SendSync 标记 trait。有关这些的更多信息,请参见 关于 SendSync 的部分

这是可以的,因为

  • 只有当它是唯一引用该数据的 Arc 时(仅在 Drop 中发生),你才能获得对 Arc 内部值的可变引用
  • 我们使用原子操作进行共享可变引用计数
unsafe impl<T: Sync + Send> Send for Arc<T> {}
unsafe impl<T: Sync + Send> Sync for Arc<T> {}

我们需要具有约束 T: Sync + Send,因为如果我们不提供这些约束,则可能通过 Arc 跨线程边界共享线程不安全的值,这可能会导致数据竞争或不健全。

例如,如果不存在这些约束,则 Arc<Rc<u32>> 将是 SyncSend,这意味着你可以从 Arc 中克隆 Rc 以跨线程发送它(而无需创建全新的 Rc),这将导致数据竞争,因为 Rc 不是线程安全的。

获取 ArcInner

要将 NonNull<T> 指针解引用为 &T,我们可以调用 NonNull::as_ref。这与典型的 as_ref 函数不同,它是 unsafe 的,因此我们必须像这样调用它

unsafe { self.ptr.as_ref() }

我们将在代码中多次使用此代码段(通常带有相关的 let 绑定)。

这种不安全性是允许的,因为当此 Arc 存在时,我们保证内部指针是有效的。

Deref

好的。现在我们可以创建 Arc(并且很快就可以正确地克隆和销毁它们),但是我们如何访问内部数据?

我们现在需要的是 Deref 的实现。

我们需要导入该 trait

use std::ops::Deref;

这是实现

impl<T> Deref for Arc<T> {
    type Target = T;

    fn deref(&self) -> &T {
        let inner = unsafe { self.ptr.as_ref() };
        &inner.data
    }
}

很简单,对吧? 这只是将 NonNull 指针解引用到 ArcInner<T>,然后获取对内部数据的引用。

代码

这是本节中的所有代码

use std::ops::Deref;

impl<T> Arc<T> {
    pub fn new(data: T) -> Arc<T> {
        // We start the reference count at 1, as that first reference is the
        // current pointer.
        let boxed = Box::new(ArcInner {
            rc: AtomicUsize::new(1),
            data,
        });
        Arc {
            // It is okay to call `.unwrap()` here as we get a pointer from
            // `Box::into_raw` which is guaranteed to not be null.
            ptr: NonNull::new(Box::into_raw(boxed)).unwrap(),
            phantom: PhantomData,
        }
    }
}

unsafe impl<T: Sync + Send> Send for Arc<T> {}
unsafe impl<T: Sync + Send> Sync for Arc<T> {}


impl<T> Deref for Arc<T> {
    type Target = T;

    fn deref(&self) -> &T {
        let inner = unsafe { self.ptr.as_ref() };
        &inner.data
    }
}