thread::spawn和thread::scope

1. move 所有权

flowchart LR %% ① move 所有权转移 subgraph Move["① move 所有权转移(不共享)"] M_Main[主线程] M_a[变量 a] M_Child[子线程] M_aa[变量 a] %% 数据流 M_Main --> M_a M_a -->|"move a"| M_Child M_Child -->|"独占修改 ✅"| M_aa --> M_Child M_a -.-> |"主线程 变量a 失效"| M_aa %% 样式 style M_Main fill:#ffe6e6,stroke:#ff4d4d style M_Child fill:#fff2cc,stroke:#ff9900 style M_a fill:#ffcccc,stroke:#ff0000 end
use std::thread;

fn main() {
    let a = String::from("hello");
    thread::spawn(move || {
        println!("{}", a); // 独占使用
    }).join().unwrap();
    // println!("{}", a); // ❌ 已被 move
}

2. Arc<Mutex>

flowchart LR %% ② Arc<Mutex>(共享 + 并发安全) subgraph ArcMutex["② Arc< Mutex>(共享 + 并发安全)"] A_Main[主线程] A_Child1[子线程1] A_Child2[子线程2] A_Data["Arc< Mutex< T>>(共享数据)"] A_Main -->|"Arc::clone(&a)"| A_Child1 A_Main -->|"Arc::clone(&a)"| A_Child2 A_Child1 -->|"lock() 修改 ✅"| A_Data A_Child2 -->|"lock() 修改 ✅"| A_Data style A_Main fill:#e6ffe6,stroke:#00cc44 style A_Child1 fill:#cce6ff,stroke:#0066ff style A_Child2 fill:#cce6ff,stroke:#0066ff style A_Data fill:#ccffcc,stroke:#00aa00 end
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let data = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..2 {
        let d = Arc::clone(&data);
        handles.push(thread::spawn(move || {
          	// d.lock() 返回的是一个 MutexGuard,这是一个 RAII 类型的智能指针。
            let mut num = d.lock().unwrap();
            *num += 1;
            // 当 num 离开作用域时,MutexGuard 会被 drop,锁被释放。
        }));
    }

    for h in handles { h.join().unwrap(); }
    // 打印时你要访问 data 的内容,此时需要一个新的锁来保证线程安全。
    // 因为 Arc<Mutex<T>> 的内部数据是被 Mutex 保护的,所以每次访问都必须拿锁。
    println!("result = {}", *data.lock().unwrap()); // result = 2
}

3. thread::scope

flowchart LR %% ③ thread::scope(借用局部变量) subgraph Scope["③ thread::scope(借用局部变量)"] S_Main[主线程(作用域)] S_a[变量 a(局部变量)不可变] %% 子线程大框 subgraph Threads["子线程(作用域内)"] S_Child1[子线程1] S_Child2[子线程2] S_a1[不可变借用 变量 a(局部变量)] S_a2[不可变借用 变量 a(局部变量)] end %% 数据流 S_Main --> S_a S_a -->|"&a"| S_Child1 S_a -->|"&a"| S_Child2 S_Child1 -->|"&a 只读 ✅"| S_a1 --> S_Child1 S_Child2 -->|"&a 只读 ✅"| S_a2 --> S_Child2 %% 作用域结束自动 join S_Child1 -.->|"作用域结束自动 join"| S_Main S_Child2 -.->|"作用域结束自动 join"| S_Main %% 样式 style S_Main fill:#f0ccff,stroke:#9900ff style S_a fill:#e6ccff,stroke:#9900ff style S_Child1 fill:#f9e6ff,stroke:#9900ff style S_Child2 fill:#f9e6ff,stroke:#9900ff style Threads fill:#f2e6ff,stroke:#9900ff,stroke-width:2px end
use std::thread;

fn main() {
    let a = String::from("hello");
    thread::scope(|s| {
        s.spawn(|| println!("thread1: {}", a));
    		s.spawn(|| println!("thread2: {}", a));
    });
    println!("{}", a); // ✅ 作用域结束后还能用
}

单线程可变借用

  • 只有一个线程拿到 &mut a

  • 作用域结束前没有其他线程访问 a

thread::scope(|s| {
    s.spawn(|| {
        let a_mut = &mut a; // 独占可变借用
        a_mut.push_str(" world");
    });
});

多线程可变借用

  • Rust 不允许同时把同一个 &mut a 借给多个线程
thread::scope(|s| {
    s.spawn(|| { &mut a }); // ❌
    s.spawn(|| { &mut a }); // ❌
});

多线程安全可变共享

  • 如果多个线程都要修改同一个变量,就必须用 同步原语
    • Mutex<T>(互斥锁)
    • RwLock<T>(读写锁)
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let data = Arc::new(Mutex::new(0));

    thread::scope(|s| {
        for _ in 0..2 {
            let d = Arc::clone(&data);
            s.spawn(move || {
                let mut num = d.lock().unwrap();
                *num += 1;
            });
        }
    });

    println!("result = {}", *data.lock().unwrap());
}
posted @ 2026-08-10 10:03  lxd670  阅读(7)  评论(0)    收藏  举报