|
|
@ -2,11 +2,11 @@
|
|
|
|
Rust所有权机制要求一个值只能有一个所有者,在大多数情况下,都没有问题,但是考虑以下情况:
|
|
|
|
Rust所有权机制要求一个值只能有一个所有者,在大多数情况下,都没有问题,但是考虑以下情况:
|
|
|
|
|
|
|
|
|
|
|
|
- 在图数据结构中,多个边可能会拥有同一个节点,该节点直到没有边指向它时,才应该被释放清理
|
|
|
|
- 在图数据结构中,多个边可能会拥有同一个节点,该节点直到没有边指向它时,才应该被释放清理
|
|
|
|
- 在多线程中,多个线程可能会指向同一个数据,但是你受限于Rust的安全机制,你又无法同时的可变借用该数据
|
|
|
|
- 在多线程中,多个线程可能会持有同一个数据,但是你受限于Rust的安全机制,无法同时获取该数据的可变引用。
|
|
|
|
|
|
|
|
|
|
|
|
以上场景不是很常见,但是一旦遇到,就非常棘手,为了解决此类问题,Rust在所有权机制之外又引入了额外的措施来简化相应的实现:通过引用计数的方式,允许一个数据资源在同一时刻拥有多个所有者。
|
|
|
|
以上场景不是很常见,但是一旦遇到,就非常棘手,为了解决此类问题,Rust在所有权机制之外又引入了额外的措施来简化相应的实现:通过引用计数的方式,允许一个数据资源在同一时刻拥有多个所有者。
|
|
|
|
|
|
|
|
|
|
|
|
这种实现机制就是`Rc`和`Arc`,前者适用于单线程,后者适用于多线程。由于二者大部分时间都是相同,因此本章将以`Rc`作为讲解主体,对于`Arc`的不同之处,也将进行单独讲解。
|
|
|
|
这种实现机制就是`Rc`和`Arc`,前者适用于单线程,后者适用于多线程。由于二者大部分情况下都相同,因此本章将以`Rc`作为讲解主体,对于`Arc`的不同之处,另外进行单独讲解。
|
|
|
|
|
|
|
|
|
|
|
|
## Rc<T>
|
|
|
|
## Rc<T>
|
|
|
|
引用计数(reference counting),顾名思义,通过记录一个数据被引用的次数来确定该数据是否正在被使用。当引用次数归零时,就代表该数据不再被使用,因此可以被清理释放。
|
|
|
|
引用计数(reference counting),顾名思义,通过记录一个数据被引用的次数来确定该数据是否正在被使用。当引用次数归零时,就代表该数据不再被使用,因此可以被清理释放。
|
|
|
@ -75,7 +75,7 @@ fn main() {
|
|
|
|
#### 不可变引用
|
|
|
|
#### 不可变引用
|
|
|
|
事实上,`Rc<T>`是指向底层数据的不可变的引用,因此你无法通过它来修改数据,这也符合Rust的借用规则:要么多个不可变借用,要么一个可变借用。
|
|
|
|
事实上,`Rc<T>`是指向底层数据的不可变的引用,因此你无法通过它来修改数据,这也符合Rust的借用规则:要么多个不可变借用,要么一个可变借用。
|
|
|
|
|
|
|
|
|
|
|
|
但是可以修改数据也是非常有用的,只不过我们需要配合其它数据类型来一起使用,例如内部可变性的`RefCell<T>`类型以及互斥锁`Mutex<T>`。事实上,在多线程编程中,`Arc`跟`Mutext`锁的组合使用非常常见,既可以让我们在不同的线程中共享数据,又允许在各个线程中对其进行修改。
|
|
|
|
但是可以修改数据也是非常有用的,只不过我们需要配合其它数据类型来一起使用,例如内部可变性的`RefCell<T>`类型以及互斥锁`Mutex<T>`。事实上,在多线程编程中,`Arc`跟`Mutext`锁的组合使用非常常见,它们既可以让我们在不同的线程中共享数据,又允许在各个线程中对其进行修改。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 一个综合例子
|
|
|
|
#### 一个综合例子
|
|
|
@ -85,13 +85,13 @@ use std::rc::Rc;
|
|
|
|
|
|
|
|
|
|
|
|
struct Owner {
|
|
|
|
struct Owner {
|
|
|
|
name: String,
|
|
|
|
name: String,
|
|
|
|
// ...other fields
|
|
|
|
// ...其它字段
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Gadget {
|
|
|
|
struct Gadget {
|
|
|
|
id: i32,
|
|
|
|
id: i32,
|
|
|
|
owner: Rc<Owner>,
|
|
|
|
owner: Rc<Owner>,
|
|
|
|
// ...other fields
|
|
|
|
// ...其它字段
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -125,7 +125,7 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
以上代码很好的展示了`Rc<T>`的用途,当然你也可以用借用的方式,但是实现起来就会复杂的多,而且随着工具在各个代码中的四处使用,引用生命周期也将变得更加复杂,毕竟结构体中的引用类型,总是令人不那么愉快,对不?
|
|
|
|
以上代码很好的展示了`Rc<T>`的用途,当然你也可以用借用的方式,但是实现起来就会复杂的多,而且随着`Gadget`在代码的各个地方使用,引用生命周期也将变得更加复杂,毕竟结构体中的引用类型,总是令人不那么愉快,对不?
|
|
|
|
|
|
|
|
|
|
|
|
#### Rc简单总结
|
|
|
|
#### Rc简单总结
|
|
|
|
|
|
|
|
|
|
|
@ -167,7 +167,7 @@ error[E0277]: `Rc<String>` cannot be sent between threads safely
|
|
|
|
好在天无绝人之路,一起来看看Rust为我们提供的功能一致但是多线程安全的`Arc`。
|
|
|
|
好在天无绝人之路,一起来看看Rust为我们提供的功能一致但是多线程安全的`Arc`。
|
|
|
|
|
|
|
|
|
|
|
|
## Arc
|
|
|
|
## Arc
|
|
|
|
`Arc`是`Atomic Rc`的缩写,顾名思义:原子化的`Rc<T>`智能指针。原子化是一种并发原语,我们在后续章节会进行深入讲解,这里你只要知道它能保证我们的数据能够安全的被线程间共享即可。
|
|
|
|
`Arc`是`Atomic Rc`的缩写,顾名思义:原子化的`Rc<T>`智能指针。原子化是一种并发原语,我们在后续章节会进行深入讲解,这里你只要知道它能保证我们的数据能够安全的在线程间共享即可。
|
|
|
|
|
|
|
|
|
|
|
|
#### Arc的性能损耗
|
|
|
|
#### Arc的性能损耗
|
|
|
|
你可能好奇,为何不直接使用`Arc`,还要画蛇添足弄一个`Rc`,还有Rust的基本数据类型、标准库数据类型为什么不自动实现原子化操作?
|
|
|
|
你可能好奇,为何不直接使用`Arc`,还要画蛇添足弄一个`Rc`,还有Rust的基本数据类型、标准库数据类型为什么不自动实现原子化操作?
|
|
|
@ -194,7 +194,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 总结
|
|
|
|
## 总结
|
|
|
|
在Rust中,所有权机制保证了一个数据只会有一个所有者,如果你想要在图数据结构、或者多线程等中使用,这种机制会成为极大的阻碍。 好在Rust为我们提供了智能指针`Rc`和`Arc`,使用它们就能实现多个所有者共享一个数据的功能。
|
|
|
|
在Rust中,所有权机制保证了一个数据只会有一个所有者,但如果你想要在图数据结构、多线程等场景中共享数据,这种机制会成为极大的阻碍。好在Rust为我们提供了智能指针`Rc`和`Arc`,使用它们就能实现多个所有者共享一个数据的功能。
|
|
|
|
|
|
|
|
|
|
|
|
`Rc`和`Arc`的区别在于,后者是原子化实现的引用计数,因此是线程安全的,可以用于多线程中共享数据。
|
|
|
|
`Rc`和`Arc`的区别在于,后者是原子化实现的引用计数,因此是线程安全的,可以用于多线程中共享数据。
|
|
|
|
|
|
|
|
|
|
|
|