RUST:无畏并发
Rust编译器在编译时会拒绝不正确的代码,使代码中并发缺陷可以在开发过程中被及时修复,这一特性被称为:无畏并发
(值得一提的是:这里Rust团队在文档中将并发与并行统称为并发)
使用线程同时运行代码
由于不同线程在执行过程中的具体顺序无法确定,可能会导致以下问题
-
多个线程以不一致的顺序访问数据或资源时产生的竞争状态(race condition)
-
两个线程同时尝试获取对方持有的资源时产生的死锁(deadlock)
-
只会出现在特定情形下且难以稳定重现和修复的bug
使用spawn创建新线程
thread::spawn()接收一个闭包作为参数,该闭包就是将会在新线程中运行的代码
比如下面这段代码就创建了一个新的线程
use std::thread;
use std::time::Duration;
fn main(){
thread::spawn(||{
for i in 1..10{
println!("sleep {} hours a day",i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5{
println!("sleep {} days a week",i);
thread::sleep(Duration::from_millis(1));
}
}
输出结果如下:
sleep 1 days a week
sleep 1 hours a day
sleep 2 hours a day
sleep 2 days a week
sleep 3 days a week
sleep 3 hours a day
sleep 4 hours a day
sleep 4 days a week
sleep 5 hours a day
但是由于调用thread::sleep会强制当前线程停止运行一段时间,这两个线程会交替运行,但由于主线程的停止,新线程会同步停止,这样就无法完整输出1-10的所有数字,所以引入了接下来的JoinHandle
使用join句柄等待所有线程结束
thread::spawn的返回值类型是一个自持有所有权的JoinHandle,调用其的join方法可以阻塞当前线程直到对应的新线程执行结束
我们只需要对上面的代码稍加修改就可以使新线程完整进行了
use std::thread;
use std::time::Duration;
fn main(){
let handle:thread::JoinHandle<()>=thread::spawn(||{//thread::JoinHandle<()>是这里handle的类型
for i in 1..10{
println!("sleep {} hours a day",i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5{
println!("sleep {} days a week",i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap();//主线程本应在此就结束,但是被handle的join方法阻塞,直到上面的新线程运行完才结束
}
输出结果:
sleep 1 days a week
sleep 1 hours a day
sleep 2 hours a day
sleep 2 days a week
sleep 3 hours a day
sleep 3 days a week
sleep 4 hours a day
sleep 4 days a week
sleep 5 hours a day
sleep 6 hours a day
sleep 7 hours a day
sleep 8 hours a day
sleep 9 hours a day
接下来我们看一下rustlings上的一道练习题
use std::thread;
use std::time::{Duration, Instant};
fn main() {
let mut handles = vec![];
for i in 0..10 {
handles.push(thread::spawn(move || {
let start = Instant::now();
thread::sleep(Duration::from_millis(250));
println!("thread {} is complete", i);
start.elapsed().as_millis()
}));
}
let mut results: Vec<u128> = vec![];
for handle in handles {
//TODO
}
if results.len() != 10 {
panic!("Oh no! All the spawned threads did not finish!");
}
println!();
for (i, result) in results.into_iter().enumerate() {
println!("thread {} took {}ms", i, result);
}
}
这里只需要简单修改一下就可以
let mut results: Vec<u128> = vec![];
for handle in handles {
handle.join().map(|mills|{
results.push(mills)
});
}
简单解释一下:map方法接受一个闭包,并创建一个新的迭代器,在新迭代器的每个元素上调用该闭包
由于这里join方法返回的是一个Result类型,所以我们用map方法将其中Ok()中的值push进results并返回就好了