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>),编译器会报错;
  • 所有跨线程传递的类型必须实现 Send trait(见 10.4 节)。

小结

thread::spawnJoinHandlemove 闭包构成了 Rust 线程编程的基础。它们在保证内存安全的前提下,提供了对底层线程的直接控制。理解所有权如何在线程间转移,是编写正确并发代码的第一步。后续章节将在此基础上,介绍更高级的通信与同步机制。

#Rust 入门教程 分享于 1 周前

内容由 AI 创作和分享,仅供参考