前言
在结束线程的学习过后,笔者继续学习了关于多线程通信的工具,也就是通道,利用通道实现多种不同类型的多线程之间的通信,也由此引发了一些笔者的思考,具体如下所述。
同样学习视频地址为 ->> b站令狐一冲Rust进阶
一、管道是什么?
Rust中一个实现消息传递并发的主要工具是通道。通道由两部分组成,一个是发送端,一个是接收端,发送端用来发送消息,接收端用来接收消息。发送者或者接收者任一被丢弃时就可以认为通道被关闭了。
二、使用步骤
1.通道类型
1.通过mpsc::channel()创建通道,mpsc 是多个生产者,一个消费者;
2.通过SPMC::channel()创建通道,spmc 是一个生产者,多个消费者;
注意:创建通道后返回的是发送者和接收者(生产者和消费者),示例:
let (tx,rx) = mpsc::channel();
let (tx,rx) = spmc::channel();
在下文中将主要使用多个生产者单个消费者为主要示例
2.通道的定义与使用
代码如下:
use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hello,thread!");
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("Main thread got {}",received);
}
在上述代码段中,笔者通过 mpsc::channel() 定义了 (tx , rx) ,也就是发送端和接收端,同时创建了一个子线程,在线程中定义了一个String类型的变量并通过 tx 发送端将该变量送入了通道,而在主线程中定义的 rx 接收端就会接收到通道中的变量,最后进行输出,编译运行结果如下:
root@ThinkPad-T540p:~/learn_rust/learn_thread_channel# cargo run
Compiling learn_thread_channel v0.1.0 (/root/learn_rust/learn_thread_channel)
Finished dev [unoptimized + debuginfo] target(s) in 0.82s
Running `target/debug/learn_thread_channel`
Main thread got hello,thread!
编译运行是顺利的,成功实现了主线程与子线程之间的通信,但是在上篇关于线程的文章中,笔者曾经遇到过子线程还未结束就因为主线程结束而被强制中断的情况,当时使用了 join() 解决了这个问题,但是在这里并未使用join(),但是主线程却一直等待子线程对val变量的发送,这是有违昨天所学的知识的。
而针对该问题,在视频中也得到了答案,是因为接收端rx使用的recv()函数。这里使用的recv()方法,会保持主线程为阻塞态,直到有一个消息到来并接收,所以上述代码段中不需要join()方法,同时也可以使用try_recv()方法,该方法不会阻塞,会立即返回。
3.通道中的所有权问题
在上述通道通信简单使用的代码段中,笔者将String型变量 val 作为发送数据送入了发送通道,而此时也就相当于将val的所有权移动到了该通道中,所以在通道将val发送后,就不能再单独使用val了。
4.多信息多线程模拟
结束上述学习后,除了对于通信的简单实现,还对单个子线程发送多个信息与多个线程发送信息的情况进行了实现,代码如下:
use std::thread;
use std::sync::mpsc;
use std::time::Duration;
fn main() {
let (tx,rx) = mpsc::channel();
thread::spawn(move || {
let vals = vec![
String::from("Hello"),
String::from("nice"),
String::from("to"),
String::from("meet"),
String::from("you!"),
];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
for recv in rx {
println!("{}",recv);
}
println!("Hello, world!");
}
上述代码使用了vec容器,在单个子线程中向主线程发送了多条信息,编译运行结果如下:
root@ThinkPad-T540p:~/learn_rust/learn_thread_channel3# cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
Running `target/debug/learn_thread_channel3`
Hello
nice
to
meet
you!
Hello, world!
同样的,再实现多个线程的情况,代码如下:
use std::thread;
use std::sync::mpsc;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
let tx1 = mpsc::Sender::clone(&tx);
let tx2 = mpsc::Sender::clone(&tx);
thread::spawn(move || {
let vals = vec![
String::from("1"),
String::from("2"),
String::from("3"),
String::from("4"),
];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
thread::spawn(move || {
let vals = vec![
String::from("a"),
String::from("b"),
String::from("c"),
String::from("d"),
];
for val in vals {
tx1.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
thread::spawn(move || {
let vals = vec![
String::from("A"),
String::from("B"),
String::from("C"),
String::from("D"),
];
for val in vals {
tx2.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
for recv in rx {
println!("{}",recv);
}
println!("Hello, world!");
}
编译运行结果如下:
root@ThinkPad-T540p:~/learn_rust/learn_thread_channel4# cargo run
Compiling learn_thread_channel4 v0.1.0 (/root/learn_rust/learn_thread_channel4)
Finished dev [unoptimized + debuginfo] target(s) in 1.74s
Running `target/debug/learn_thread_channel4`
1
a
A
b
2
B
3
c
C
d
4
D
Hello, world!
可以观察到多个线程发送的信息依次被主线程接收。
总结
以上就是我对通道学习的全过程,最近写了一个线程间循环通信回复的小例子,待完善后会补充到文中以供学习参考。还请各位读者不吝赐教,批评指正。