Merge pull request #444 from Nangos/patch-33

Update ch16-01-threads.md
This commit is contained in:
KaiserY 2020-07-19 21:51:53 +08:00 committed by GitHub
commit 1f41826190
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -19,7 +19,7 @@ Rust 尝试减轻使用线程的负面影响。不过在多线程上下文中编
每一个模型都有其优势和取舍。对于 Rust 来说最重要的取舍是运行时支持。**运行时**_Runtime_是一个令人迷惑的概念其在不同上下文中可能有不同的含义。
在当前上下文中,**运行时** 代表二进制文件中包含的由语言自身提供的代码。这些代码根据语言的不同可大可小,不过任何非汇编语言都会有一定数量的运行时代码。为此,通常人们说一个语言 “没有运行时”,一般意味着 “小运行时”。更小的运行时拥有更少的功能不过其优势在于更小的二进制输出,这使其易于在更多上下文中与其他语言相结合。虽然很多语言觉得增加运行时来换取更多功能没有什么问题,但是 Rust 需要做到几乎没有运行时,同时为了保持高性能必能够调用 C 语言,这点也是不能妥协的。
在当前上下文中,**运行时** 代表二进制文件中包含的由语言自身提供的代码。这些代码根据语言的不同可大可小,不过任何非汇编语言都会有一定数量的运行时代码。为此,通常人们说一个语言 “没有运行时”,一般意味着 “小运行时”。更小的运行时拥有更少的功能不过其优势在于更小的二进制输出,这使其易于在更多上下文中与其他语言相结合。虽然很多语言觉得增加运行时来换取更多功能没有什么问题,但是 Rust 需要做到几乎没有运行时,同时为了保持高性能必能够调用 C 语言,这点也是不能妥协的。
绿色线程的 M:N 模型需要更大的语言运行时来管理这些线程。因此Rust 标准库只提供了 1:1 线程模型实现。由于 Rust 是较为底层的语言,如果你愿意牺牲性能来换取抽象,以获得对线程运行更精细的控制及更低的上下文切换成本,你可以使用实现了 M:N 线程模型的 crate。
@ -68,7 +68,7 @@ hi number 5 from the spawned thread!
`thread::sleep` 调用强制线程停止执行一小段时间,这会允许其他不同的线程运行。这些线程可能会轮流运行,不过并不保证如此:这依赖操作系统如何调度线程。在这里,主线程首先打印,即便新创建线程的打印语句位于程序的开头,甚至即便我们告诉新建的线程打印直到 `i` 等于 9 ,它在主线程结束之前也只打印到了 5。
如果运行代码只看到了主线程的输出,或没有出现重叠打印的现象,尝试增加 range 的数值来增加操作系统切换线程的机会。
如果运行代码只看到了主线程的输出,或没有出现重叠打印的现象,尝试增大区间 (变量 `i` 的范围) 来增加操作系统切换线程的机会。
#### 使用 `join` 等待所有线程结束
@ -164,7 +164,7 @@ hi number 3 from the main thread!
hi number 4 from the main thread!
```
稍微考虑一下将 `join` 放置于何处这样一个细节会影响线程是否同时运行。
诸如将 `join` 放置于何处这样的小细节,会影响线程是否同时运行。
### 线程与 `move` 闭包
@ -247,7 +247,7 @@ variables), use the `move` keyword
| ^^^^^^^
```
通过在闭包之前增加 `move` 关键字,我们强制闭包获取其使用的值的所有权,而不是任由 Rust 推断它应该借用值。示例 16-5 中展示的对示例 16-3 代码的修改,可以按照我们的预期编译并运行:
通过在闭包之前增加 `move` 关键字,我们强制闭包获取其使用的值的所有权,而不是任由 Rust 推断它应该借用值。示例 16-5 中展示的对示例 16-3 代码的修改,可以按照我们的预期编译并运行:
<span class="filename">文件名: src/main.rs</span>
@ -267,7 +267,7 @@ fn main() {
<span class="caption">示例 16-5: 使用 `move` 关键字强制获取它使用的值的所有权</span>
那么如果使用了 `move` 闭包,示例 16-4 中主线程调用了 `drop` 的代码会发生什么呢?不幸的是,我们会因为示例 16-4 尝试进行由于不同的原因所不允许的操作而得到不同的错误。如果为闭包增加 `move`,将会把 `v` 移动进闭包的环境中,如此将不能在主线程中对其调用 `drop` 了。我们会得到如下不同的编译错误:
那么如果使用了 `move` 闭包,示例 16-4 中主线程调用了 `drop` 的代码会发生什么呢?加了 `move` 就搞定了吗?不幸的是,我们会得到一个不同的错误,因为示例 16-4 所尝试的操作由于一个不同的原因而不被允许。如果为闭包增加 `move`,将会把 `v` 移动进闭包的环境中,如此将不能在主线程中对其调用 `drop` 了。我们会得到如下不同的编译错误:
```text
error[E0382]: use of moved value: `v`
@ -283,6 +283,6 @@ error[E0382]: use of moved value: `v`
not implement the `Copy` trait
```
Rust 的所有权规则又一次帮助了我们!示例 16-3 中的错误是因为 Rust 是保守的并只会为线程借用 `v`,这意味着主线程理论上可能使新建线程的引用无效。通过告诉 Rust 将 `v` 的所有权移动到新建线程,我们向 Rust 保证主线程不会再使用 `v`。如果对示例 16-4 也做出如此修改,那么当在主线程中使用 `v` 时就会违反所有权规则。 `move` 关键字覆盖了 Rust 默认保守的借用它不允许我们违反所有权规则。
Rust 的所有权规则又一次帮助了我们!示例 16-3 中的错误是因为 Rust 是保守的并只会为线程借用 `v`,这意味着主线程理论上可能使新建线程的引用无效。通过告诉 Rust 将 `v` 的所有权移动到新建线程,我们向 Rust 保证主线程不会再使用 `v`。如果对示例 16-4 也做出如此修改,那么当在主线程中使用 `v` 时就会违反所有权规则。 `move` 关键字覆盖了 Rust 默认保守的借用,但它不允许我们违反所有权规则。
现在我们对线程和线程 API 有了基本的了解,让我们讨论一下使用线程实际可以 **做** 什么吧。