mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-14 04:41:49 +08:00
commit
1f41826190
@ -19,7 +19,7 @@ Rust 尝试减轻使用线程的负面影响。不过在多线程上下文中编
|
|||||||
|
|
||||||
每一个模型都有其优势和取舍。对于 Rust 来说最重要的取舍是运行时支持。**运行时**(_Runtime_)是一个令人迷惑的概念,其在不同上下文中可能有不同的含义。
|
每一个模型都有其优势和取舍。对于 Rust 来说最重要的取舍是运行时支持。**运行时**(_Runtime_)是一个令人迷惑的概念,其在不同上下文中可能有不同的含义。
|
||||||
|
|
||||||
在当前上下文中,**运行时** 代表二进制文件中包含的由语言自身提供的代码。这些代码根据语言的不同可大可小,不过任何非汇编语言都会有一定数量的运行时代码。为此,通常人们说一个语言 “没有运行时”,一般意味着 “小运行时”。更小的运行时拥有更少的功能不过其优势在于更小的二进制输出,这使其易于在更多上下文中与其他语言相结合。虽然很多语言觉得增加运行时来换取更多功能没有什么问题,但是 Rust 需要做到几乎没有运行时,同时为了保持高性能必需能够调用 C 语言,这点也是不能妥协的。
|
在当前上下文中,**运行时** 代表二进制文件中包含的由语言自身提供的代码。这些代码根据语言的不同可大可小,不过任何非汇编语言都会有一定数量的运行时代码。为此,通常人们说一个语言 “没有运行时”,一般意味着 “小运行时”。更小的运行时拥有更少的功能不过其优势在于更小的二进制输出,这使其易于在更多上下文中与其他语言相结合。虽然很多语言觉得增加运行时来换取更多功能没有什么问题,但是 Rust 需要做到几乎没有运行时,同时为了保持高性能必须能够调用 C 语言,这点也是不能妥协的。
|
||||||
|
|
||||||
绿色线程的 M:N 模型需要更大的语言运行时来管理这些线程。因此,Rust 标准库只提供了 1:1 线程模型实现。由于 Rust 是较为底层的语言,如果你愿意牺牲性能来换取抽象,以获得对线程运行更精细的控制及更低的上下文切换成本,你可以使用实现了 M:N 线程模型的 crate。
|
绿色线程的 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。
|
`thread::sleep` 调用强制线程停止执行一小段时间,这会允许其他不同的线程运行。这些线程可能会轮流运行,不过并不保证如此:这依赖操作系统如何调度线程。在这里,主线程首先打印,即便新创建线程的打印语句位于程序的开头,甚至即便我们告诉新建的线程打印直到 `i` 等于 9 ,它在主线程结束之前也只打印到了 5。
|
||||||
|
|
||||||
如果运行代码只看到了主线程的输出,或没有出现重叠打印的现象,尝试增加 range 的数值来增加操作系统切换线程的机会。
|
如果运行代码只看到了主线程的输出,或没有出现重叠打印的现象,尝试增大区间 (变量 `i` 的范围) 来增加操作系统切换线程的机会。
|
||||||
|
|
||||||
#### 使用 `join` 等待所有线程结束
|
#### 使用 `join` 等待所有线程结束
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ hi number 3 from the main thread!
|
|||||||
hi number 4 from the main thread!
|
hi number 4 from the main thread!
|
||||||
```
|
```
|
||||||
|
|
||||||
稍微考虑一下将 `join` 放置于何处这样一个细节会影响线程是否同时运行。
|
诸如将 `join` 放置于何处这样的小细节,会影响线程是否同时运行。
|
||||||
|
|
||||||
### 线程与 `move` 闭包
|
### 线程与 `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>
|
<span class="filename">文件名: src/main.rs</span>
|
||||||
|
|
||||||
@ -267,7 +267,7 @@ fn main() {
|
|||||||
|
|
||||||
<span class="caption">示例 16-5: 使用 `move` 关键字强制获取它使用的值的所有权</span>
|
<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
|
```text
|
||||||
error[E0382]: use of moved value: `v`
|
error[E0382]: use of moved value: `v`
|
||||||
@ -283,6 +283,6 @@ error[E0382]: use of moved value: `v`
|
|||||||
not implement the `Copy` trait
|
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 有了基本的了解,让我们讨论一下使用线程实际可以 **做** 什么吧。
|
现在我们对线程和线程 API 有了基本的了解,让我们讨论一下使用线程实际可以 **做** 什么吧。
|
||||||
|
Loading…
Reference in New Issue
Block a user