@ -1,15 +1,14 @@
## 使用消息传递在线程间传送数据
## 使用消息传递在线程间传送数据
> [ch16-02-message-passing.md ](https://github.com/rust-lang/book/blob/master/src/ch16-02-message-passing.md )
> [ch16-02-message-passing.md ](https://github.com/rust-lang/book/blob/master/src/ch16-02-message-passing.md ) > < br >
> < br >
> commit 26565efc3f62d9dacb7c2c6d0f5974360e459493
> commit 26565efc3f62d9dacb7c2c6d0f5974360e459493
一个日益流行的确保安全并发的方式是 ** 消息传递**( *message passing* ),这里线程或 actor 通过发送包含数据的消息来相互沟通。这个思想来源于 [Go 编程语言文档中 ](http://golang.org/doc/effective_go.html ) 的口号: “不要共享内存来通讯; 而是要通讯来共享内存。”( “Do not communicate by
一个日益流行的确保安全并发的方式是 ** 消息传递**( _message passing_ ),这里线程或 actor 通过发送包含数据的消息来相互沟通。这个思想来源于 [Go 编程语言文档中 ](http://golang.org/doc/effective_go.html ) 的口号: “不要共享内存来通讯; 而是要通讯来共享内存。”( “Do not communicate by
sharing memory; instead, share memory by communicating.”)
sharing memory; instead, share memory by communicating.”)
Rust 中一个实现消息传递并发的主要工具是 ** 通道**( *channel* ),一个 Rust 标准库提供了其实现的编程概念。你可以将其想象为一个水流的通道,比如河流或小溪。如果你将诸如橡皮鸭或小船之类的东西放入其中,它们会顺流而下到达下游。
Rust 中一个实现消息传递并发的主要工具是 ** 通道**( _channel_ ),一个 Rust 标准库提供了其实现的编程概念。你可以将其想象为一个水流的通道,比如河流或小溪。如果你将诸如橡皮鸭或小船之类的东西放入其中,它们会顺流而下到达下游。
编程中的通道有两部分组成, 一个发送者( transmitter) 和一个接收者( receiver) 。发送者一端位于上游位置, 在这里可以将橡皮鸭放入河中, 接收者部分则位于下游, 橡皮鸭最终会漂流至此。代码中的一部分调用发送者的方法以及希望发送的数据, 另一部分则检查接收端收到到达的消息。当发送者或接收者任一被丢弃时可以认为通道被 ** 关闭**( *closed* )了
编程中的通道有两部分组成, 一个发送者( transmitter) 和一个接收者( receiver) 。发送者一端位于上游位置, 在这里可以将橡皮鸭放入河中, 接收者部分则位于下游, 橡皮鸭最终会漂流至此。代码中的一部分调用发送者的方法以及希望发送的数据, 另一部分则检查接收端收到到达的消息。当发送者或接收者任一被丢弃时可以认为通道被 ** 关闭**( _closed_ )了
这里,我们将开发一个程序,它会在一个线程生成值向通道发送,而在另一个线程会接收值并打印出来。这里会通过通道在线程间发送简单值来演示这个功能。一旦你熟悉了这项技术,就能使用通道来实现聊天系统或利用很多线程进行分布式计算并将部分计算结果发送给一个线程进行聚合。
这里,我们将开发一个程序,它会在一个线程生成值向通道发送,而在另一个线程会接收值并打印出来。这里会通过通道在线程间发送简单值来演示这个功能。一旦你熟悉了这项技术,就能使用通道来实现聊天系统或利用很多线程进行分布式计算并将部分计算结果发送给一个线程进行聚合。
@ -27,9 +26,9 @@ fn main() {
< span class = "caption" > 示例 16-6: 创建一个通道,并将其两端赋值给 `tx` 和 `rx` </ span >
< span class = "caption" > 示例 16-6: 创建一个通道,并将其两端赋值给 `tx` 和 `rx` </ span >
这里使用 `mpsc::channel` 函数创建一个新的通道;`mpsc` 是 ** 多个生产者,单个消费者**( *multiple producer, single consumer* ) 的缩写。简而言之, Rust 标准库实现通道的方式意味着一个通道可以有多个产生值的 ** 发送**( *sending*)端,但只能有一个消费这些值的 ** 接收**( *receiving* )端。想象一下多条小河小溪最终汇聚成大河:所有通过这些小河发出的东西最后都会来到大河的下游。目前我们以单个生产者开始,但是当示例可以工作后会增加多个生产者。
这里使用 `mpsc::channel` 函数创建一个新的通道;`mpsc` 是 ** 多个生产者,单个消费者**( _multiple producer, single consumer_ ) 的缩写。简而言之, Rust 标准库实现通道的方式意味着一个通道可以有多个产生值的 ** 发送**( _sending_) 端, 但只能有一个消费这些值的 ** 接收**( _receiving_ )端。想象一下多条小河小溪最终汇聚成大河:所有通过这些小河发出的东西最后都会来到大河的下游。目前我们以单个生产者开始,但是当示例可以工作后会增加多个生产者。
`mpsc::channel` 函数返回一个元组:第一个元素是发送端,而第二个元素是接收端。由于历史原因,`tx` 和 `rx` 通常作为 ** 发送者**( *transmitter*)和 ** 接收者**( *receiver* )的缩写,所以这就是我们将用来绑定这两端变量的名字。这里使用了一个 `let` 语句和模式来解构了此元组;第十八章会讨论 `let` 语句中的模式和解构。如此使用 `let` 语句是一个方便提取 `mpsc::channel` 返回的元组中一部分的手段。
`mpsc::channel` 函数返回一个元组:第一个元素是发送端,而第二个元素是接收端。由于历史原因,`tx` 和 `rx` 通常作为 ** 发送者**( _transmitter_) 和 ** 接收者**( _receiver_ )的缩写,所以这就是我们将用来绑定这两端变量的名字。这里使用了一个 `let` 语句和模式来解构了此元组;第十八章会讨论 `let` 语句中的模式和解构。如此使用 `let` 语句是一个方便提取 `mpsc::channel` 返回的元组中一部分的手段。
让我们将发送端移动到一个新建线程中并发送一个字符串,这样新建线程就可以和主线程通讯了,如示例 16-7 所示。这类似与在河的上游扔下一只橡皮鸭或从一个线程向另一个线程发送聊天信息:
让我们将发送端移动到一个新建线程中并发送一个字符串,这样新建线程就可以和主线程通讯了,如示例 16-7 所示。这类似与在河的上游扔下一只橡皮鸭或从一个线程向另一个线程发送聊天信息:
@ -53,7 +52,7 @@ fn main() {
这里再次使用 `thread::spawn` 来创建一个新线程并使用 `move` 将 `tx` 移动到闭包中这样新建线程就拥有 `tx` 了。新建线程需要拥有通道的发送端以便能向通道发送消息。
这里再次使用 `thread::spawn` 来创建一个新线程并使用 `move` 将 `tx` 移动到闭包中这样新建线程就拥有 `tx` 了。新建线程需要拥有通道的发送端以便能向通道发送消息。
通道的发送端有一个 `send` 方法用来获取需要放入通道的值。`send` 方法返回一个 `Result<T, E>` 类型,所以如果接收端已经被丢弃了,将没有发送值的目标,所以发送操作会返回错误。在这个例子中,出错的时候调用 `unwrap` 产生 panic。过对于一个真实程序, 需要合理的 处理它:回到第九章复习正确处理错误的策略。
通道的发送端有一个 `send` 方法用来获取需要放入通道的值。`send` 方法返回一个 `Result<T, E>` 类型,所以如果接收端已经被丢弃了,将没有发送值的目标,所以发送操作会返回错误。在这个例子中,出错的时候调用 `unwrap` 产生 panic。过对于一个真实程序, 需要合理地 处理它:回到第九章复习正确处理错误的策略。
在示例 16-8 中,我们在主线程中从通道的接收端获取值。这类似于在河的下游捞起橡皮鸭或接收聊天信息:
在示例 16-8 中,我们在主线程中从通道的接收端获取值。这类似于在河的下游捞起橡皮鸭或接收聊天信息:
@ -78,7 +77,7 @@ fn main() {
< span class = "caption" > 示例 16-8: 在主线程中接收并打印内容 “hi”< / span >
< span class = "caption" > 示例 16-8: 在主线程中接收并打印内容 “hi”< / span >
通道的接收端有两个有用的方法:`recv` 和 `try_recv` 。这里,我们使用了 `recv` ,它是 *receive* 的缩写。这个方法会阻塞主线程执行直到从通道中接收一个值。一旦发送了一个值,`recv` 会在一个 `Result<T, E>` 中返回它。当通道发送端关闭,`recv` 会返回一个错误表明不会再有新的值到来了。
通道的接收端有两个有用的方法:`recv` 和 `try_recv` 。这里,我们使用了 `recv` ,它是 _receive_ 的缩写。这个方法会阻塞主线程执行直到从通道中接收一个值。一旦发送了一个值,`recv` 会在一个 `Result<T, E>` 中返回它。当通道发送端关闭,`recv` 会返回一个错误表明不会再有新的值到来了。
`try_recv` 不会阻塞,相反它立刻返回一个 `Result<T, E>` : `Ok` 值包含可用的信息,而 `Err` 值代表此时没有任何消息。如果线程在等待消息过程中还有其他工作时使用 `try_recv` 很有用:可以编写一个循环来频繁调用 `try_recv` ,再有可用消息时进行处理,其余时候则处理一会其他工作直到再次检查。
`try_recv` 不会阻塞,相反它立刻返回一个 `Result<T, E>` : `Ok` 值包含可用的信息,而 `Err` 值代表此时没有任何消息。如果线程在等待消息过程中还有其他工作时使用 `try_recv` 很有用:可以编写一个循环来频繁调用 `try_recv` ,再有可用消息时进行处理,其余时候则处理一会其他工作直到再次检查。
@ -188,7 +187,7 @@ Got: thread
### 通过克隆发送者来创建多个生产者
### 通过克隆发送者来创建多个生产者
之前我们提到了`mpsc`是 *multiple producer, single consumer* 的缩写。可以运用 `mpsc` 来扩展示例 16-11 中的代码来以创建都向同一接收者发送值的多个线程。这可以通过克隆通道的发送端在来做到,如示例 16-11 所示:
之前我们提到了`mpsc`是 _multiple producer, single consumer_ 的缩写。可以运用 `mpsc` 来扩展示例 16-11 中的代码来以创建都向同一接收者发送值的多个线程。这可以通过克隆通道的发送端在来做到,如示例 16-11 所示:
< span class = "filename" > 文件名: src/main.rs< / span >
< span class = "filename" > 文件名: src/main.rs< / span >