1. 概述
音讯传递是一种很流行且能确保安全并发的技术,在这种机制里线程(或Actor)经过互相发送音讯(数据)来进行通信。Go言语中有一句名言:“不要用同享内存来通信,要用通信来同享内存”,Go言语这种并发机制就体现了这个思想。
Rust也供给了一种根据音讯传递的并发方式,在rust里运用规范库供给的Channel
来完成。Channel
包含发送端和接纳端,咱们能够经过调用发送端的办法来发送数据,接纳端会查看和接纳到达的数据。假如发送端和接纳端的任意一端被丢掉了,那么Channel
就封闭了。
2. 运用Channel
2.1 在不同线程之间创立和接纳数据
运用mpsc::channel
函数来创立Channel,mpsc
表明multiple producer, singer consumer(多个生产者、一个顾客),即有多个发送端,但只有一个接纳端。调用该函数将回来一个元组,元组里的元素别离是发送端、接纳端。
如下示例代码:
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("Got: {}", received);
}
顾客的recv
办法一向会阻塞当时线程,直到接纳到音讯为止。
2.2 发送端的send办法
该办法的参数为想要发送的数据,回来值为Result<T, E>
,假如有问题(例如接纳端已经被丢掉),将回来一个过错。
2.3 接纳端的办法
recv
办法阻挠当时线程执行,直到Channel中有值被送来。一旦收到值,就会回来Result<T>
,一切这个管道的一切发送端都封闭了,就会收到一个过错。
try_recv
办法不会阻塞当时的线程,假如有数据到达,回来OK
,里面包含着数据,否则回来过错。咱们通常会运用循环来查看try_recv
的成果,假如音讯还没有来,咱们也能够执行其他的操作。
2.4 channel和一切权搬运
一切权先音讯传递中非常重要,能帮你补全编写安全、并发的代码。
咱们先看以下的示例代码:
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap();
// 下面一行代码将会报错,由于一切权已经被搬运
println("val is {}", val);
});
let received = rx.recv().unwrap();
println!("Got: {}", received);
}
在上面的示例代码中,借用了已移动的值,因此会产生编译过错。所以一切权机制会帮助咱们编写编写安全、并发的代码。
2.5 发送多个值
咱们经过发送多个值,就能够看到接纳者在等待的进程。
如下示例代码:
use std::sync::mpsc;
use std::{thread, vec};
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let vals = vec![
String::from("hi"),
String::from("from"),
String::from("the"),
String::from("thread"),
];
// 循环别离发送四个字符串
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_millis(1000));
}
});
// 咱们把接纳端当作迭代器来运用,这样就不需求显式调用recv办法
for received in rx {
println!("Got: {}", received);
}
}
运转以上的代码,咱们将看到接纳端在等待音讯的进程。
2.6 经过克隆创立多个发送者
经过调用mpsc::Sender::clone
函数能够克隆发送者。
如下示例代码:
use std::sync::mpsc;
use std::{thread, vec};
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
let tx1 = mpsc::Sender::clone(&tx);
thread::spawn(move || {
let vals = vec![
String::from("1: hi"),
String::from("1: from"),
String::from("1: the"),
String::from("1: thread"),
];
// 循环别离发送四个字符串
for val in vals {
tx1.send(val).unwrap();
thread::sleep(Duration::from_millis(200));
}
});
thread::spawn(move || {
let vals = vec![
String::from("hi"),
String::from("from"),
String::from("the"),
String::from("thread"),
];
// 循环别离发送四个字符串
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_millis(200));
}
});
// 咱们把接纳端当作迭代器来运用,这样就不需求显式调用recv办法
for received in rx {
println!("Got: {}", received);
}
}
在以上的示例代码中,咱们经过两个子线程由两个发送者来发数据。并在主线程中运用接纳者接纳数据,能够经进程序运转成果看到由两个发送者发送的数据被交替输出。