
Rust 通道由两部分组成:Sender<T>
和 Receiver<T>
。这两部分通过通道相互连接,但你只能看到端点:
Rust
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
tx.send(10).unwrap();
tx.send(20).unwrap();
println!("Received: {:?}", rx.recv());
println!("Received: {:?}", rx.recv());
let tx2 = tx.clone();
tx2.send(30).unwrap();
println!("Received: {:?}", rx.recv());
}
// Output
// Received: Ok(10)
// Received: Ok(20)
// Received: Ok(30)
mpsc
代表多生产者、单消费者(Multi-Producer, Single-Consumer),想象一下我们的电脑屏幕,很多设备信号都可以放到屏幕上显示,但是屏幕只有一个。Sender
和 SyncSender
实现了 Clone
(这样你就可以创建多个生产者),但 Receiver
没有实现。
send()
和 recv()
方法返回 Result
。如果返回 Err
,这意味着对应的 Sender
或 Receiver
已被丢弃,并且通道已关闭。
通过 mpsc::channel()
,你可以创建一个无界且异步的通道(unbounded channel):
Rust
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let thread_id = thread::current().id();
for i in 0..10 {
tx.send(format!("Message {i}")).unwrap();
println!("{thread_id:?}: sent Message {i}");
}
println!("{thread_id:?}: done");
});
thread::sleep(Duration::from_millis(100));
for msg in rx.iter() {
println!("Main: got {msg}");
}
}
输出如下:
makefile
// Output
ThreadId(2): sent Message 0
ThreadId(2): sent Message 1
ThreadId(2): sent Message 2
ThreadId(2): sent Message 3
ThreadId(2): sent Message 4
ThreadId(2): sent Message 5
ThreadId(2): sent Message 6
ThreadId(2): sent Message 7
ThreadId(2): sent Message 8
ThreadId(2): sent Message 9
ThreadId(2): done
Main: got Message 0
Main: got Message 1
Main: got Message 2
Main: got Message 3
Main: got Message 4
Main: got Message 5
Main: got Message 6
Main: got Message 7
Main: got Message 8
Main: got Message 9
如果要在多线程的情况下发送消息呢?
Rust
fn main() {
let (tx, rx) = mpsc::channel();
// 注意这里的写法,多使用了一对大括号,在最后返回的闭包。
thread::spawn({
let tx = tx.clone();
move || {
for i in 0..10 {
tx.send(i).unwrap();
}
}
});
thread::spawn(move || {
for i in 10..20 {
tx.send(i).unwrap();
}
});
for i in rx {
println!("received {}", i);
}
}
当 tx
的所有权被释放的时候,通道也就关闭了。所以,你可以尝试一下,如果在第二个线程的地方,也使用类似克隆的方式,你会发现,最后这个 for
循环,会一直阻塞。
一个无界通道会根据存储待处理消息的需要分配足够的空间。send()
方法不会阻塞调用线程。
如果通道已关闭,调用 send()
方法将终止并返回一个错误(这就是它返回 Result
的原因)。当接收器被释放时,通道就会关闭。
对于有界(同步)通道(bounded channel),send()
可能会阻塞当前线程:
Rust
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::sync_channel(3);
thread::spawn(move || {
let thread_id = thread::current().id();
for i in 0..10 {
tx.send(format!("Message {i}")).unwrap();
println!("{thread_id:?}: sent Message {i}");
}
println!("{thread_id:?}: done");
});
thread::sleep(Duration::from_millis(100));
for msg in rx.iter() {
println!("Main: got {msg}");
}
}
调用 send()
会阻塞当前线程,直到通道中有空间来存放新消息。如果没有人从通道中读取消息,线程可能会无限期阻塞。
与无界通道一样,如果通道已关闭,调用 send()
会因错误而中止。
大小为零的有界通道称为"会合通道"。每次调用 send()
都会阻塞当前线程,直到另一个线程调用 recv()
。