长安的 Rust 入门教程长安的 Rust 入门教程
首页
基础教程
进阶内容
Rust 官网
编程指南
首页
基础教程
进阶内容
Rust 官网
编程指南
  • 进阶内容

    • 🚀 进阶内容
    • 第1章 - 泛型
    • 第2章 - Trait
    • 第3章 - 生命周期
    • 第4章 - 智能指针
    • 第5章 - 并发编程

第5章 - 并发编程

嗨,朋友!我是长安。

恭喜你来到了进阶教程的最后一章!这一章我们要学习 Rust 最强大的特性之一——并发编程。

Rust 的类型系统和所有权系统让你可以写出安全的并发代码,大部分并发错误都能在编译时被捕获!

🤔 什么是并发?

并发(Concurrency)是指程序的不同部分独立执行。
并行(Parallelism)是指程序的不同部分同时执行。

长安说

想象你在做饭:

  • 并发:你一会儿切菜,一会儿烧水,交替进行
  • 并行:你和朋友同时做饭,你切菜,朋友烧水

Rust 的并发特性同时支持这两种场景!

🧵 创建线程

基本用法

使用 thread::spawn 创建新线程:

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

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

注意

上面的代码可能不会打印完全部子线程的输出,因为主线程结束时会关闭所有子线程!

等待线程完成

使用 join 等待线程执行完毕:

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

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..=5 {
            println!("子线程: {}", i);
            thread::sleep(Duration::from_millis(500));
        }
    });
    
    for i in 1..=3 {
        println!("主线程: {}", i);
        thread::sleep(Duration::from_millis(300));
    }
    
    handle.join().unwrap();  // 等待子线程完成
    println!("所有线程完成!");
}

📦 线程间传递数据

使用 move 闭包

use std::thread;

fn main() {
    let v = vec![1, 2, 3];
    
    let handle = thread::spawn(move || {
        println!("向量: {:?}", v);
    });
    
    // println!("{:?}", v);  // ❌ 错误!v 的所有权已转移
    
    handle.join().unwrap();
}

move 关键字强制闭包获取环境变量的所有权。

📬 消息传递

Rust 提供了通道(Channel)来实现线程间通信。

创建通道

use std::sync::mpsc;  // multiple producer, single consumer
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    
    thread::spawn(move || {
        let val = String::from("你好");
        tx.send(val).unwrap();
    });
    
    let received = rx.recv().unwrap();
    println!("收到: {}", received);
}
  • tx (transmitter):发送者
  • rx (receiver):接收者
  • send:发送消息
  • recv:接收消息(阻塞)

发送多个消息

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();
    
    thread::spawn(move || {
        let messages = vec![
            String::from("消恗1"),
            String::from("消恗2"),
            String::from("消恗3"),
        ];
        
        for msg in messages {
            tx.send(msg).unwrap();
            thread::sleep(Duration::from_millis(500));
        }
    });
    
    for received in rx {
        println!("收到: {}", received);
    }
}

多个生产者

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

fn main() {
    let (tx, rx) = mpsc::channel();
    let tx1 = tx.clone();  // 克隆发送者
    
    thread::spawn(move || {
        tx.send(String::from("线程1")).unwrap();
    });
    
    thread::spawn(move || {
        tx1.send(String::from("线程2")).unwrap();
    });
    
    for received in rx {
        println!("{}", received);
    }
}

🔒 共享状态

Mutex - 互斥锁

Mutex<T> 允许多个线程访问同一个数据,但同一时间只有一个线程可以修改。

use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);
    
    {
        let mut num = m.lock().unwrap();
        *num = 6;
    }  // 锁在这里自动释放
    
    println!("m = {:?}", m);
}

Arc - 原子引用计数

还记得 Rc<T> 吗?它只能用于单线程。多线程要用 Arc<T>(Atomic Reference Counted)。

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 counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("结果: {}", *counter.lock().unwrap());
}

长安说

Arc<Mutex<T>> 是 Rust 中多线程共享可变数据的标准模式!

  • Arc:多个线程拥有所有权
  • Mutex:保证同一时间只有一个线程修改

🎯 实战例子:并发计数器

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

#[derive(Debug)]
struct Counter {
    count: Mutex<i32>,
}

impl Counter {
    fn new() -> Self {
        Counter {
            count: Mutex::new(0),
        }
    }
    
    fn increment(&self) {
        let mut count = self.count.lock().unwrap();
        *count += 1;
    }
    
    fn get(&self) -> i32 {
        *self.count.lock().unwrap()
    }
}

fn main() {
    let counter = Arc::new(Counter::new());
    let mut handles = vec![];
    
    for _ in 0..100 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            counter.increment();
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("最终计数: {}", counter.get());
}

🚀 并行计算示例

让我们用多线程计算一个向量的和:

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

fn main() {
    let numbers: Vec<i32> = (1..=1000).collect();
    let sum = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    let chunk_size = 250;
    
    for chunk in numbers.chunks(chunk_size) {
        let sum = Arc::clone(&sum);
        let chunk = chunk.to_vec();
        
        let handle = thread::spawn(move || {
            let local_sum: i32 = chunk.iter().sum();
            let mut total = sum.lock().unwrap();
            *total += local_sum;
        });
        
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("1到1000的和: {}", *sum.lock().unwrap());
}

🚨 Send 和 Sync Trait

Rust 的并发安全来自两个特殊的 Trait:

Send

Send trait 表示类型的所有权可以在线程间转移。

几乎所有 Rust 类型都实现了 Send,但 Rc<T> 没有!

Sync

Sync trait 表示类型可以安全地被多个线程引用。

如果 &T 实现了 Send,那么 T 就实现了 Sync。

长安说

你几乎不需要手动实现 Send 和 Sync。Rust 会自动为你的类型实现它们(如果安全)。

如果你的类型不安全,编译器会阻止你在多线程中使用!

📊 并发模式对比

模式适用场景优点缺点
消息传递线程间需要通信安全,无锁需要复制数据
共享状态需要修改同一数据高效,无需复制需要加锁

💡 小结

在这一章,我们学会了:

  • 使用 thread::spawn 创建线程
  • 使用 join 等待线程完成
  • 使用 通道(mpsc::channel)实现消息传递
  • 使用 Mutex<T> 实现共享状态
  • 使用 Arc<T> 在线程间共享所有权
  • Arc<Mutex<T>> 是多线程共享可变数据的标准模式
  • Send 和 Sync trait 保证并发安全

核心要点:

  • Rust 的类型系统在编译时防止数据竞争
  • 消息传递和共享状态是两种主要并发模式
  • Arc 用于多线程,Rc 用于单线程
  • Mutex 保证数据同时只被一个线程访问

长安的建议

并发编程在其他语言中通常很难,但在 Rust 中相对简单!因为:

  1. 编译器帮你检查:大部分并发错误都能在编译时发现
  2. 模式清晰:消息传递和共享状态两种模式
  3. 类型安全:Send 和 Sync 自动保证安全

建议:

  • 优先使用消息传递(更安全)
  • 必须时才用共享状态
  • 用 Arc<Mutex<T>> 处理共享可变数据

🎉 恭喜你!

你已经完成了整个 Rust 教程!

从第一章认识 Rust,到最后掌握并发编程,你已经掌握了 Rust 的核心概念:

✅ 所有权系统
✅ 引用与借用
✅ 结构体与枚举
✅ 泛型与 Trait
✅ 生命周期
✅ 智能指针
✅ 并发编程

接下来,你可以:

  1. 做项目:把学到的知识应用到实际项目中
  2. 阅读代码:阅读优秀的 Rust 项目代码
  3. 深入学习:学习 async/await、宏系统等高级特性
  4. 加入社区:参与 Rust 开源社区

记住:Rust 的学习曲线比较陡,但一旦掌握,你会发现它真的非常优雅和强大!

继续努力,用 Rust 创造更多精彩的项目吧!🚀

—— 长安

💪 练习题

  1. 创建 5 个线程,每个线程打印自己的 ID
  2. 使用通道让主线程收集所有子线程的结果
  3. 用 Arc<Mutex<Vec<i32>>> 创建一个多线程共享的列表,多个线程向其中添加数据
  4. 实现一个多线程的文件读取器,将文件分成多个块并行处理
答案示例
use std::sync::{Arc, Mutex, mpsc};
use std::thread;

// 练习1
fn exercise1() {
    let mut handles = vec![];
    
    for i in 0..5 {
        let handle = thread::spawn(move || {
            println!("线程 ID: {}", i);
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
}

// 练习2
fn exercise2() {
    let (tx, rx) = mpsc::channel();
    let mut handles = vec![];
    
    for i in 0..5 {
        let tx = tx.clone();
        let handle = thread::spawn(move || {
            tx.send(i * i).unwrap();
        });
        handles.push(handle);
    }
    
    drop(tx);  // 关闭原始发送者
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    for received in rx {
        println!("收到: {}", received);
    }
}

// 练习3
fn exercise3() {
    let list = Arc::new(Mutex::new(Vec::new()));
    let mut handles = vec![];
    
    for i in 0..10 {
        let list = Arc::clone(&list);
        let handle = thread::spawn(move || {
            let mut l = list.lock().unwrap();
            l.push(i);
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("列表: {:?}", *list.lock().unwrap());
}

fn main() {
    println!("=== 练习1 ===");
    exercise1();
    
    println!("\n=== 练习2 ===");
    exercise2();
    
    println!("\n=== 练习3 ===");
    exercise3();
}
最近更新: 2025/12/26 18:01
Contributors: 王长安
Prev
第4章 - 智能指针