Skip to content

Commit

Permalink
revise chapter 6 (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
hycinth22 authored Feb 28, 2024
1 parent 68236f0 commit 9996ab4
Showing 1 changed file with 9 additions and 9 deletions.
18 changes: 9 additions & 9 deletions 6_Building_Our_Own_Arc.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct ArcData<T> {

接下来是 `Arc<T>` 结构体本身,它实际上仅是一个指向(共享的)`ArcData<T>` 的指针。

使用 `Box<ArcDate<T>>` 作为包装器,并使用标准的 Box 来处理 `ArcData<T>` 的内存分配可能很诱人。然而,Box 表示独占所有权,并不是共享所有权。我们不能使用引用,因为我们不仅要借用其他所有权的数据,并且它的生命周期(“直到此 Arc 的最后一个克隆被丢弃”)无法直接表示为 Rust 的生命周期。
使用 `Box<ArcData<T>>` 作为包装器,并使用标准的 Box 来处理 `ArcData<T>` 的内存分配可能很诱人。然而,Box 表示独占所有权,并不是共享所有权。我们不能使用引用,因为我们不仅要借用其他所有权的数据,并且它的生命周期(“直到此 Arc 的最后一个克隆被丢弃”)无法直接表示为 Rust 的生命周期。

相反,我们将不得不使用指针,并手动处理内存分配以及所有权的概念。我们将使用 `std::ptr::NonNull<T>`,而不是 `*mut T``*const T`,它表示一个永远不会为空的指向 T 的指针。这样,使用 None 的空指针表示 `Option<Arc<T>>``Arc<T>` 的大小相同。

Expand Down Expand Up @@ -161,7 +161,7 @@ fn test() {
}

// 创建两个 Arc,共享一个对象,包含一个字符串
// 和一个 DetecDrop,以当它被丢弃时去检测。
// 和一个 DetectDrop,以当它被丢弃时去检测。
let x = Arc::new(("hello", DetectDrop));
let y = x.clone();

Expand Down Expand Up @@ -211,7 +211,7 @@ fn test() {
然而,我们可以有条件地允许独占访问。我们可以创建一个方法,如果引用计数为 1,则提供 `&mut T`,这证明没有其他 Arc 对象可以用来访问相同的数据。

<a class="indexterm" id="index-Arc-getmut"></a>
该函数我们将称它为 `get_mut`,它必须接受一个 `&mute Self` 以确保没有其他的相同的东西使用 Arc 获取 T。如果这个 Arc 仍然可以共享,知道只有一个 Arc 对象是没有意义的。
该函数我们将称它为 `get_mut`,它必须接受一个 `&mut Self` 以确保没有其他的相同的东西使用 Arc 获取 T。如果这个 Arc 仍然可以共享,知道只有一个 Arc 对象是没有意义的。

<a class="indexterm" id="index-happens-beforerelationships-inArc-2"></a>
我们需要使用 acquire 内存排序去确保之前拥有 Arc 克隆的线程不再访问数据。我们需要与导致引用计数为 1 的每个单独的 `drop` 建立一个 happens-before 关系。
Expand Down Expand Up @@ -248,7 +248,7 @@ fn test() {

标准库的 Arc 提供了解决这个问题的办法:`Weak<T>``Weak<T>`(也被称为 *weak 指针*),行为有点像 `Arc<T>`,但是并不会阻止对象被丢弃。T 可以在多个 `Arc<T>``Weak<T>` 对象之间共享,但是当所有 `Arc<T>` 对象都消失时,不管是否还有 `Weak<T>` 对象,T 都会被丢弃。

着意味着 `Weak<T>` 可以没有 T 而存在,因此无法像 Arc 那样无条件地提供 `&T`。然而,为了获取给定 `Weak<T>` 中的 T,可以通过 `Arc<T>``upgrade()` 方法来升级。这个方法返回一个 `Option<Arc<T>>`,如果 T 已经被丢弃,则返回 None。
这意味着 `Weak<T>` 可以没有 T 而存在,因此无法像 Arc 那样无条件地提供 `&T`。然而,为了获取给定 `Weak<T>` 中的 T,可以通过 `Arc<T>``upgrade()` 方法来升级。这个方法返回一个 `Option<Arc<T>>`,如果 T 已经被丢弃,则返回 None。

在基于 Arc 的结构中,可以使用 Weak 打破循环引用。例如,在树结构中的子节点使用 Weak,而不是使用 Arc 来引用它们的父节点。然后,尽管子节点存在,也不会阻止父节点被丢弃。

Expand All @@ -271,7 +271,7 @@ struct ArcData<T> {
}
```

如果我们认为 `Weak<T>` 时保持 `ArcData<T>` 存活的对象,那么将 `Arc<T>` 实现为包含 `Weak<T>` 的结构体可能是有意义的,因为 `Arc<T>` 需要做相同的事情,而且还有更多的功能。
如果我们认为 `Weak<T>` 是保持 `ArcData<T>` 存活的对象,那么将 `Arc<T>` 实现为包含 `Weak<T>` 的结构体可能是有意义的,因为 `Arc<T>` 需要做相同的事情,而且还有更多的功能。

```rust
pub struct Arc<T> {
Expand Down Expand Up @@ -318,7 +318,7 @@ impl<T> Weak<T> {
}
```

`Arc<T>` 的 Deref 实现中,我们现在不得不使用 `UnsafeCell::get()` 拉锯得到 cell 内容的指针,并使用不安全的代码去承诺它此时可以共享。我们也需要 `as_ref().unwrap()` 去获取 `Option<T>` 引用。我们不必担心引发 panic,因为只有在没有 Arc 对象时 Option 才会为 None。
`Arc<T>` 的 Deref 实现中,我们现在不得不使用 `UnsafeCell::get()` 拉取得到 cell 内容的指针,并使用不安全的代码去承诺它此时可以共享。我们也需要 `as_ref().unwrap()` 去获取 `Option<T>` 引用。我们不必担心引发 panic,因为只有在没有 Arc 对象时 Option 才会为 None。

```rust
impl<T> Deref for Arc<T> {
Expand Down Expand Up @@ -422,7 +422,7 @@ impl<T> Arc<T> {
}
```

接下来:是升级 weak 指针。当数据仍然存在时,才能升级 Weak 到 Arc。如果仅剩下 Weak 指针,则没有数据通过 Arc 共享了。因此,我们需要递增 Arc 的计数器,但只能在计数器不为 0 时才能这样做。我们将使用「比较并交换」循环([第二章的“比较并交换操作”](./2_Atomics.md#比较并交换操作))来做这些。
接下来:是升级 Weak 指针。当数据仍然存在时,才能升级 Weak 到 Arc。如果仅剩下 Weak 指针,则没有数据通过 Arc 共享了。因此,我们需要递增 Arc 的计数器,但只能在计数器不为 0 时才能这样做。我们将使用「比较并交换」循环([第二章的“比较并交换操作”](./2_Atomics.md#比较并交换操作))来做这些。

与之前一样,对于递增引用计数,relaxed 内存排序是好的。在这个原子操作之前或之后,没有其他变量的操作需要严格执行。

Expand Down Expand Up @@ -608,7 +608,7 @@ impl<T> Deref for Arc<T> {

fn deref(&self) -> &T {
// 安全性:因为有一个 Arc 包裹 data,
// data 存在不能呗共享
// data 存在,并且可能被共享
unsafe { &*self.data().data.get() }
}
}
Expand Down Expand Up @@ -708,7 +708,7 @@ impl<T> Weak<T> {

到目前为止,这与我们之前的实现差距是非常小的。然而,问题出现在我们仍然要实现最后两个方法:`downgrade``get_mut`

与之前不同,`get_mut` 方法现在需要检查是否都设置为 1,以决定是否指进剩下一个 `Arc<T>` 以及没有保留 `Weak<T>`,因为一个 weak 指针计数现在可以表示多个 `Arc<T>` 指针。读取计数器是发生在(稍微)不同时间的两个分开的操作,所以我们不得不非常小心,以确保不会错过任何并发的 downgrade,就像我们在[“优化”](#优化)一节示例的开头所见。
与之前不同,`get_mut` 方法现在需要检查是否都设置为 1,以判断是否只存在一个 `Arc<T>` 以及没有`Weak<T>`,因为一个 weak 指针计数现在可以表示多个 `Arc<T>` 指针。读取计数器是发生在(稍微)不同时间的两个分开的操作,所以我们不得不非常小心,以确保不会错过任何并发的 downgrade,就像我们在[“优化”](#优化)一节示例的开头所见。

如果我们首先检查 `data_ref_count` 是 1,那么我们在检查另一个计数器之前,可能错过随后的 `upgrade()`。但是,如果我们首先检查 `alloc_ref_count` 是 1,那么在检查另一个计数器之前,可能错过随后的 `downgrade()`

Expand Down

0 comments on commit 9996ab4

Please sign in to comment.