通道(Channel)
迄今为止,我们创建的所有线程都是短生命周期的。它们获取输入、执行计算、返回结果并关闭。但对于我们的票务管理系统,我们想要采取不同的方式:一种客户端-服务器架构。
我们将拥有一个长时间运行的服务器线程,负责管理我们的状态,即存储的票务信息。随后,我们将拥有多个客户端线程。每个客户端都能向这个有状态的线程发送命令和查询,以便改变其状态(例如,添加新票据)或检索信息(例如,获取票据的状态)。客户端线程将以并发的方式运行。
通信
到目前为止,我们所进行的父子线程间通信还相当有限:
- 生成的线程从父级上下文中借用或消耗数据,
- 当线程连接时,生成的线程向父级返回数据。
这对于客户端-服务器设计来说是不够的。客户端需要能够在服务器线程启动后,能够与其发送和接收数据。我们可以通过使用**通道(Channels)**来解决这个问题。
通道
Rust的标准库在其std::sync::mpsc
模块中提供了**多生产者单消费者(Multi-Producer, Single-Consumer, 简称mpsc)**通道。通道有两种类型:有界和无界。目前我们先关注无界版本,但稍后会讨论它们各自的优缺点。
创建通道的代码如下所示:
#![allow(unused)] fn main() { use std::sync::mpsc::channel; let (sender, receiver) = channel(); }
这样你就得到了一个发送器(sender)和一个接收器(receiver)。你可以在发送器上调用send
方法将数据推入通道,在接收器上调用recv
方法从通道中拉取数据。
多个发送者
Sender
是可以克隆的:我们可以创建多个发送器(比如,为每个客户端线程一个),它们都将数据推送到同一个通道中。
相反,Receiver
是不可克隆的:对于给定的通道,只能存在一个接收器。
这就是mpsc(多生产者单消费者)的含义!
消息类型
Sender
和Receiver
都对一个类型参数T
进行了泛型。这是可以在通道上传输的消息的类型。
它可以是一个u64
、结构体、枚举等。
错误处理
send
和recv
都可能失败。如果接收器已经被丢弃,则send
会返回错误;如果所有发送器都被丢弃且通道为空,则recv
会返回错误。
换句话说,当通道实际上已经关闭时,send
和recv
就会发生错误。