Merge pull request #572 from zongzi531/hotfix/sync2

Fix typo in concurrency-with-threads/sync2.md & send-sync.md
pull/573/head
Sunface 3 years ago committed by GitHub
commit 6eb2084f1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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`,例如文中的原生指针例子

@ -166,7 +166,7 @@ Y = 3; Y *= 2;
X = 2; } X = 2; }
``` ```
还是可能出现`Y=2`,因为`Main`线程中的`X`和`Y`被同步到其它 CPU 缓存中的顺序未必一致。 还是可能出现`Y = 2`,因为`Main`线程中的`X`和`Y`被同步到其它 CPU 缓存中的顺序未必一致。
#### 限定内存顺序的 5 个规则 #### 限定内存顺序的 5 个规则

Loading…
Cancel
Save