@ -27,7 +27,7 @@ error[E0277]: `Rc<i32>` cannot be sent between threads safely
= help: within `[closure@src/main.rs:5:27: 7:6]` , the trait `Send` is not implemented for `Rc<i32>`
= help: within `[closure@src/main.rs:5:27: 7:6]` , the trait `Send` is not implemented for `Rc<i32>`
```
```
表面原因是`Rc`无法在线程间安全的转移,实际是编译器给予我们的那句帮助: `the trait Send is not implemented for Rc<i32>` (`Rc< i32 > `未实现`Send`特征), 那么此处的`Send`特征又是何方神圣?
表面原因是`Rc`无法在线程间安全的转移,实际是编译器给予我们的那句帮助: ```the trait `Send` is not implemented for `Rc<i32>` ``` (`Rc< i32 > `未实现`Send`特征), 那么此处的`Send`特征又是何方神圣?
## Rc 和 Arc 源码对比
## Rc 和 Arc 源码对比
@ -50,7 +50,7 @@ unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
`Send` 和`Sync`是 Rust 安全并发的重中之重,但是实际上它们只是标记特征(marker trait, 该特征未定义任何行为, 因此非常适合用于标记), 来看看它们的作用:
`Send` 和`Sync`是 Rust 安全并发的重中之重,但是实际上它们只是标记特征(marker trait, 该特征未定义任何行为, 因此非常适合用于标记), 来看看它们的作用:
- 实现`Send`的类型可以在线程间安全的传递其所有权
- 实现`Send`的类型可以在线程间安全的传递其所有权
- 实现了 `Sync`的类型可以在线程间安全的共享(通过引用)
- 实现`Sync`的类型可以在线程间安全的共享(通过引用)
这里还有一个潜在的依赖:一个类型要在线程间安全的共享的前提是,指向它的引用必须能在线程间传递。因为如果引用都不能被传递,我们就无法在多个线程间使用引用去访问同一个数据了。
这里还有一个潜在的依赖:一个类型要在线程间安全的共享的前提是,指向它的引用必须能在线程间传递。因为如果引用都不能被传递,我们就无法在多个线程间使用引用去访问同一个数据了。
@ -62,7 +62,7 @@ unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
unsafe impl< T: ? Sized + Send + Sync > Sync for RwLock< T > {}
unsafe impl< T: ? Sized + Send + Sync > Sync for RwLock< T > {}
```
```
首先`RwLock`可以在线程间安全的共享,那它肯定是实现了`Sync`,但是我们的关注点不在这里。众多 周知,`RwLock`可以并发的读,说明其中的值`T`必定也可以在线程间共享,那`T`必定要实现`Sync`。
首先`RwLock`可以在线程间安全的共享,那它肯定是实现了`Sync`,但是我们的关注点不在这里。众所 周知,`RwLock`可以并发的读,说明其中的值`T`必定也可以在线程间共享,那`T`必定要实现`Sync`。
果不其然,上述代码中,`T`的特征约束中就有一个`Sync`特征,那问题又来了,`Mutex`是不是相反?再来看看:
果不其然,上述代码中,`T`的特征约束中就有一个`Sync`特征,那问题又来了,`Mutex`是不是相反?再来看看:
@ -84,7 +84,7 @@ unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
- `UnsafeCell` 不是`Sync`,因此`Cell`和`RefCell`也不是
- `UnsafeCell` 不是`Sync`,因此`Cell`和`RefCell`也不是
- `Rc` 两者都没实现(因为内部的引用计数器不是线程安全的)
- `Rc` 两者都没实现(因为内部的引用计数器不是线程安全的)
当然,如果是自定义的复合类型,那没实现那哥俩的就较为常见了:**只要复合类型中有一个成员不是`Send`或`Sync`,那么该符 合类型也就不是`Send`或`Sync`**。
当然,如果是自定义的复合类型,那没实现那哥俩的就较为常见了:**只要复合类型中有一个成员不是`Send`或`Sync`,那么该复 合类型也就不是`Send`或`Sync`**。
**手动实现 `Send` 和 `Sync` 是不安全的**,通常并不需要手动实现 Send 和 Sync trait, 实现者需要使用`unsafe`小心维护并发安全保证。
**手动实现 `Send` 和 `Sync` 是不安全的**,通常并不需要手动实现 Send 和 Sync trait, 实现者需要使用`unsafe`小心维护并发安全保证。
@ -106,7 +106,7 @@ fn main() {
}
}
```
```
报错跟之前无二: `*mut u8 cannot be sent between threads safely` , 但是有一个问题,我们无法为其直接实现`Send`特征,好在可以用[`newtype`类型](../custom-type.md#newtype) :`struct MyBox(*mut u8);`。
报错跟之前无二: ``` `*mut u8` cannot be sent between threads safely``` , 但是有一个问题,我们无法为其直接实现`Send`特征,好在可以用[`newtype`类型](../into-types /custom-type.md#newtype) :`struct MyBox(*mut u8);`。
还记得之前的规则吗:复合类型中有一个成员没实现`Send`,该复合类型就不是`Send`,因此我们需要手动为它实现:
还记得之前的规则吗:复合类型中有一个成员没实现`Send`,该复合类型就不是`Send`,因此我们需要手动为它实现:
@ -191,6 +191,6 @@ unsafe impl Sync for MyBox {}
通过上面的两个原生指针的例子,我们了解了如何实现`Send`和`Sync`,以及如何只实现`Send`而不实现`Sync`,简单总结下:
通过上面的两个原生指针的例子,我们了解了如何实现`Send`和`Sync`,以及如何只实现`Send`而不实现`Sync`,简单总结下:
1. 实现`Send`的类型可以在线程间安全的传递其所有权, 实现`Sync`的类型可以在线程间安全的共享(通过引用)
1. 实现`Send`的类型可以在线程间安全的传递其所有权, 实现`Sync`的类型可以在线程间安全的共享(通过引用)
2. 绝大部分类型都实现了`Send`和`Sync`,常见的未实现的有:原生指针、Cell/RefCell、Rc 等
2. 绝大部分类型都实现了`Send`和`Sync`,常见的未实现的有:原生指针、`Cell`、`RefCell`、`Rc` 等
3. 可以为自定义类型实现`Send`和`Sync`,但是需要`unsafe`代码块
3. 可以为自定义类型实现`Send`和`Sync`,但是需要`unsafe`代码块
4. 可以为部分 Rust 中的类型实现`Send`、`Sync`,但是需要使用`newtype`,例如文中的原生指针例子
4. 可以为部分 Rust 中的类型实现`Send`、`Sync`,但是需要使用`newtype`,例如文中的原生指针例子