Rust 并发编程

线程、`Send`/`Sync`、`Mutex`、channel、以及 `rayon` 数据并行。

Advanced
Key Points`Send` 表示能跨线程转移,`Sync` 表示能跨线程共享引用——编译器用它们挡住数据竞争。

创建线程

Rust
use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..=3 {
            println!("子线程 {}", i);
            thread::sleep(Duration::from_millis(10));
        }
    });

    for i in 1..=2 {
        println!("主线程 {}", i);
    }

    handle.join().unwrap();      // 等子线程结束
    println!("完成");
}
Output
主线程 1
子线程 1
主线程 2
子线程 2
子线程 3
完成

move 与所有权

Rust
use std::thread;

fn main() {
    let data = vec![1, 2, 3];

    // 不加 move 编译错误:闭包可能比 data 活得久
    let h = thread::spawn(move || {
        println!("{:?}", data);
        data.len()
    });

    println!("{}", h.join().unwrap());
}
Output
[1, 2, 3]
3

move hands ownership of data to the thread, so the compiler knows who frees it and there is no dangling read after the main thread returns.

Mutex

Rust
use std::sync::{Arc, Mutex};
use std::thread;

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

    for _ in 0..10 {
        let c = Arc::clone(&counter);
        handles.push(thread::spawn(move || {
            let mut n = c.lock().unwrap();
            *n += 1;
        }));
    }

    for h in handles {
        h.join().unwrap();
    }

    println!("{}", *counter.lock().unwrap());
}
Output
10

Arc is an atomic reference count enabling shared ownership; Mutex serialises access. Arc<Mutex<T>> is the standard shared-state pattern in Rust.

TypePurpose
Arc<T>多线程共享所有权
Rc<T>单线程共享所有权(更轻)
Mutex<T>互斥锁,独占访问
RwLock<T>读多写少,多读并行
AtomicUsize简单计数,无锁

channel

Rust
use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    for i in 0..3 {
        let tx = tx.clone();
        thread::spawn(move || {
            tx.send(i * 10).unwrap();
        });
    }
    drop(tx);                     // 关掉主发送端,否则 rx 永远不结束

    for v in rx {
        println!("收到 {}", v);
    }
}
Output
收到 0
收到 10
收到 20

mpsc is multi-producer single-consumer. Iterating rx blocks until every sender is dropped — forgetting drop(tx) is a classic deadlock.

Send 与 Sync

Rust
// Send:所有权可以跨线程转移
// Sync:&T 可以跨线程共享

fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}

fn main() {
    assert_send::<i32>();
    assert_send::<String>();
    assert_send::<Vec<u8>>();
    assert_sync::<i32>();
    assert_sync::<String>();

    // Rc 不是 Send:引用计数不是原子的
    // assert_send::<std::rc::Rc<i32>>();   // 编译错误
}
Output
(编译通过,无输出)

Rc<T> is not Send — its count is non-atomic, so concurrent clones would miscount. This is exactly how Rust blocks data races at compile time: the type system is the lock.

数据并行

TOML
[dependencies]
rayon = "1"
Rust
use rayon::prelude::*;

fn main() {
    let v: Vec<u64> = (1..=1_000_000).collect();

    let sum: u64 = v.par_iter().map(|x| x * 2).sum();
    println!("{}", sum);

    let mut nums: Vec<i32> = vec![5, 2, 8, 1, 9];
    nums.par_sort();
    println!("{:?}", nums);
}
Output
1000001000000
[1, 2, 5, 8, 9]

Swapping iter() for par_iter() parallelises it; rayon handles chunking and scheduling. Because the borrow checker rules out data races, this "change one word" parallelism is safe.

The costliest concurrency bugs are not deadlocks but occasionally wrong numbers. Rust moves those to compile time, so outside unsafe you rarely wonder about races — at the price of verbose Arc<Mutex<>>.

Friends & Partners