写点什么

5 分钟速读之 Rust 权威指南(三十)多线程

用户头像
码生笔谈
关注
发布于: 1 小时前
5分钟速读之Rust权威指南(三十)多线程

多线程

前端同学对于 WebWorker 肯定比较熟悉,对于计算量大的业务,我们可以将计算逻辑分配到多个线程去处理,减少主线程的压力,提高处理速度,在 rust 中启用多线程很方便,如果用 JS 的话来说,启动一个线程就像传递一个回调函数一样简单

使用 spawn 创建新线程

使用标准库中的 thread 模块创建一个 spawn 子线程:


use std::thread; // 引入threaduse std::time::Duration; // 引入time::Duration,用来创建时间类型数据
thread::spawn(|| { for i in 1..10 { println!("spawn: {}", i); // Duration::from_secs方法创建单位为秒的时间类型数据 // 使用sleep方法让spawn线程停止一秒 thread::sleep(Duration::from_secs(1)); }});
for i in 1..5 { println!("main: {}", i); // 让主线程线程停止一秒 thread::sleep(Duration::from_secs(1));}// main: 1// spawn: 1// main: 2// spawn: 2// main: 3// spawn: 3// main: 4// spawn: 4// spawn: 5
复制代码


当主线程结束时,spawn 线程也会结束,而不管其是否执行完毕,上面 spawn 线程并没有执行完成。

使用 join 等待所有线程结束

我们可以使用 thread::spawn 返回值的 join 方法控制让 main 线程去等待 spawn 线程的执行完成:


// 获取spawn线程管理工具let handle = thread::spawn(|| {  for i in 1..10 {    println!("spawn: {}", i);    thread::sleep(Duration::from_secs(1));  }});
for i in 1..5 { println!("main: {}", i); thread::sleep(Duration::from_secs(1));}// 阻塞主线程,等待spawn线程执行handle.join().unwrap();// main: 1// spawn: 1// main: 2// spawn: 2// main: 3// spawn: 3// spawn: 4// main: 4// spawn: 5// spawn: 6// spawn: 7// spawn: 8// spawn: 9
复制代码


join 会阻塞当前线程直到 handle 线程结束,阻塞(Blocking)线程意味着阻止该线程执行工作或退出。




如果将 handle.join()移动到 for 循环之前呢?可以猜一下是怎么输出的:


// 略...handle.join().unwrap();for i in 1..5 {  println!("main: {}", i);  thread::sleep(Duration::from_secs(1));}
复制代码


输出结果:


// spawn: 1// spawn: 2// spawn: 3// spawn: 4// spawn: 5// spawn: 6// spawn: 7// spawn: 8// spawn: 9// main: 1// main: 2// main: 3// main: 4
复制代码


可以看到 spawn 线程的循环会先执行完成,再执行主线程的循环。

线程与 move 闭包

上面的 spawn 线程中的代码对 main 线程中的数据没有引用,当 spawn 线程使用 main 线程中的数据时:


let n = vec![1,2,3];  let handle = thread::spawn(|| { // 报错,闭包可能比当前函数存活的时间长,但闭包它借用了'n',而'n'是当前函数拥有的  println!("来自main线程的数据: {:?}", n);});
handle.join().unwrap();
复制代码


上边闭包尝试借用 v。然而这有一个问题:rust 不知道这个新建线程会执行多久,所以无法知晓 v 的引用是否一直有效,例如:


let n = vec![1,2,3];  let handle = thread::spawn(|| {  println!("来自main线程的数据: {:?}", n);});
drop(n); // 销毁nhandle.join().unwrap();
复制代码


因为当线程中对 n 有借用,在线程还没执行的时候,后边的 drop 已经将 n 丢弃了。




我们可以通过在闭包之前增加 move 关键字,强制闭包获取其使用的 n 的所有权:


let n = vec![1, 2, 3];  let handle = thread::spawn(move || {  println!("来自main线程的数据: {:?}", n); // 来自main线程的数据: [1, 2, 3]});
handle.join().unwrap();
复制代码


上面代码中,将 n 的所有权移动到了 spawn 闭包中,所以能够正常执行。




如果仍然使用 drop 的话:


let n = vec![1, 2, 3];
let handle = thread::spawn(move || { println!("来自main线程的数据: {:?}", n);});
drop(n); // 报错,n已经被移动到上边的闭包中,不能再次在这里使用handle.join().unwrap();
复制代码


因为 n 已经被移动到了 spawn 闭包中,所以不能在后面以任何方式继续使用,即使 println!("{:?}",n),也是不允许的。

发布于: 1 小时前阅读数: 2
用户头像

码生笔谈

关注

欢迎关注「码生笔谈」公众号 2018.09.09 加入

前端、Rust干货分享,一起成为更好的Javascripter & Rustacean

评论

发布
暂无评论
5分钟速读之Rust权威指南(三十)多线程