RUST无畏并发

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并返回就好了