通道(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(多生产者单消费者)的含义!

消息类型

SenderReceiver都对一个类型参数T进行了泛型。这是可以在通道上传输的消息的类型。

它可以是一个u64、结构体、枚举等。

错误处理

sendrecv都可能失败。如果接收器已经被丢弃,则send会返回错误;如果所有发送器都被丢弃且通道为空,则recv会返回错误。

换句话说,当通道实际上已经关闭时,sendrecv就会发生错误。