10.1 线程基础:spawn、join 与 move 闭包
听
Rust 通过 std::thread 模块提供对操作系统线程的直接支持,允许程序并行执行多个任务。其并发模型建立在所有权和类型安全之上,确保线程间的数据传递不会导致数据竞争或悬垂指针。
创建线程:thread::spawn
使用 thread::spawn 可以启动一个新线程,并传入一个闭包作为该线程的执行逻辑:
use std::thread;
fn main() {
let handle = thread::spawn(|| {
for i in 1..=5 {
println!("Hello from thread: {}", i);
}
});
for i in 1..=3 {
println!("Hello from main thread: {}", i);
}
handle.join().unwrap();
}
spawn 返回一个 JoinHandle,可用于等待线程结束(通过 join())。若不调用 join(),主线程可能在子线程完成前退出,导致子线程被强制终止。
所有权与 move 闭包
新线程与主线程之间默认不共享栈数据。若要在子线程中使用主线程的变量,必须明确转移所有权。此时需使用 move 闭包:
let v = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("Vector in thread: {:?}", v); // v 被移入闭包
});
// println!("{:?}", v); // ❌ 错误:v 已被 move
handle.join().unwrap();
move 关键字强制闭包获取捕获变量的所有权(而非借用),从而满足跨线程安全的要求。对于实现了 Copy 的类型(如整数),实际是复制值;对于其他类型,则转移所有权。
线程生命周期与 join
join() 会阻塞当前线程,直到目标线程执行完毕。这常用于确保所有子线程完成后再继续主逻辑:
let handles: Vec<_> = (0..4)
.map(|i| {
thread::spawn(move || {
println!("Thread {} started", i);
thread::sleep(std::time::Duration::from_millis(100));
println!("Thread {} finished", i);
})
})
.collect();
for handle in handles {
handle.join().unwrap(); // 按创建顺序等待各线程结束
}
注意:join 的顺序不影响线程的实际执行顺序,只影响主线程等待的顺序。
安全性保障
Rust 的类型系统确保:
- 被
move到线程中的数据不会在原作用域被访问; - 若尝试在线程间共享不可安全共享的类型(如
Rc<T>),编译器会报错; - 所有跨线程传递的类型必须实现
Sendtrait(见 10.4 节)。
小结
thread::spawn、JoinHandle 和 move 闭包构成了 Rust 线程编程的基础。它们在保证内存安全的前提下,提供了对底层线程的直接控制。理解所有权如何在线程间转移,是编写正确并发代码的第一步。后续章节将在此基础上,介绍更高级的通信与同步机制。
#Rust 入门教程
分享于 1 周前
上一篇:第十章:并发编程
下一篇:10.2 消息传递:使用 mpsc 通道