文章目录Rust 多线程从入门到实战并发与并行的区别创建与管理线程最简单的多线程示例等待线程完成JoinHandle线程与所有权move 关键字的作用线程间通信消息传递mpsc 通道共享状态Arc 与 Mutex 的组合进阶原子类型与 Send/Sync 特征原子类型无锁并发Send 与 SyncRust 的线程安全标记总结Rust 多线程从入门到实战在多核处理器普及的今天多线程编程早已成为提升程序性能的核心手段。但在多线程开发中数据竞争、死锁、野指针等问题常常让人头疼调试难度极大。而 Rust 凭借其独特的所有权系统和类型安全设计将这些并发隐患扼杀在编译期实现了无畏并发Fearless Concurrency让开发者既能享受多线程的性能优势又无需担心内存安全问题。并发与并行的区别在开始编写多线程代码前我们先厘清两个容易混淆的概念这对理解Rust多线程的设计理念至关重要并发Concurrency多个任务在同一时间段内交替执行即使是单核 CPU通过任务调度也能实现比如交替处理多个网络请求核心是任务切换。并行Parallelism多个任务在同一时刻真正同时执行需要多核 CPU 的支持比如多个核心同时处理不同的计算任务核心是同时执行是真正的多任务并行。Rust 对两者都提供了完善的支持而我们常说的多线程编程既可以实现并发也可以实现并行具体取决于 CPU 核心数和程序调度。创建与管理线程最简单的多线程示例使用thread::spawn可以快速创建一个新线程该函数接收一个闭包作为参数闭包中是新线程要执行的任务usestd::thread;usestd::time::Duration;fnmain(){// 创建新线程执行闭包中的任务thread::spawn(||{foriin0..5{println!(子线程执行第{}次,i);// 让线程休眠100毫秒模拟任务耗时thread::sleep(Duration::from_millis(100));}});// 主线程执行自己的任务foriin0..2{println!(主线程执行第{}次,i);thread::sleep(Duration::from_millis(100));}thread::sleep(Duration::from_millis(1000));}运行上述代码你会发现输出结果是乱序的主线程和子线程在交替执行这就是并发的体现。等待线程完成JoinHandlethread::spawn函数的返回值是一个JoinHandleT类型其中T是子线程闭包的返回值类型。调用join()方法可以让主线程阻塞等待子线程执行完毕并获取子线程的返回值。usestd::thread;fnmain(){// 创建子线程获取 JoinHandlelethandlethread::spawn(||{letsum:i32(1..100).sum();sum// 子线程返回求和结果});// 主线程等待子线程完成并获取返回值// join() 返回 Result 类型unwrap() 用于提取成功结果// 实际开发中需处理错误情况避免 panicletresulthandle.join().unwrap();println!(子线程计算结果12...100 {},result);}这里有一个关键细节Rust 的所有权系统会严格检查线程间的数据访问。如果闭包中使用了主线程的变量必须通过 move 关键字将变量的所有权转移到子线程中否则会编译失败。线程与所有权move 关键字的作用Rust 不允许线程间共享所有权这是为了避免数据竞争因此当子线程需要使用主线程的变量时必须通过 move 将变量所有权转移。如下所示usestd::thread;fnmain(){letmsgString::from(Hello, Rust!);// 使用 move 将 msg 的所有权转移到子线程lethandlethread::spawn(move||{println!(子线程收到消息{},msg);});handle.join().unwrap();// 注意此时 msg 的所有权已转移主线程无法再使用 msg}线程间通信多线程协作的核心是通信线程之间需要交换数据才能完成复杂任务。Rust 提供了两种主流的线程间通信方式消息传递和共享状态其中消息传递是 Rust 推荐的方式不要通过共享内存来通信而要通过通信来共享内存。消息传递mpsc 通道Rust标准库中的std::sync::mpsc模块multi-producer, single-consumer提供了“多生产者、单消费者”的通道允许多个线程向一个线程发送消息实现安全的线程间通信。通道的核心是两个角色发送者Sender和接收者Receiver通过mpsc::channel()函数创建返回一个元组Sender, Receiver。usestd::sync::mpsc;usestd::thread;usestd::time::Duration;fnmain(){// 创建通道发送者tx和接收者rxlet(tx,rx)mpsc::channel();// 创建第一个子线程生产者1lettx1tx.clone();// 克隆发送者实现多生产者thread::spawn(move||{letmessagesvec![消息1,消息2,消息3];formsginmessages{tx1.send(msg).unwrap();// 发送消息thread::sleep(Duration::from_millis(500));}});// 创建第二个子线程生产者2lettx2tx.clone();thread::spawn(move||{letmessagesvec![消息A,消息B,消息C];formsginmessages{tx2.send(msg).unwrap();thread::sleep(Duration::from_millis(500));}});// 主线程作为消费者接收所有消息forreceivedinrx{println!(主线程收到{},received);}}共享状态Arc 与 Mutex 的组合虽然消息传递是推荐方式但在某些场景下比如多个线程需要读写同一个变量我们需要使用共享状态。Rust 通过原子引用计数Arc和互斥锁Mutex的组合实现安全的共享状态访问。usestd::sync::{Arc,Mutex};usestd::thread;fnmain(){// 创建共享计数器Arc 包裹 Mutex确保线程安全letcounterArc::new(Mutex::new(0));letmuthandlesvec![];// 创建10个线程每个线程对计数器加1for_in0..10{// 克隆 Arc仅克隆引用不复制数据letcounterArc::clone(counter);lethandlethread::spawn(move||{// 获取锁返回 MutexGuard实际开发中需处理可能的锁定失败这里简化使用 unwrap()letmutnumcounter.lock().unwrap();*num1;// MutexGuard 离开作用域时自动释放锁});handles.push(handle);}// 等待所有线程完成forhandleinhandles{handle.join().unwrap();}// 最终结果10个线程各加1结果为10println!(最终计数器值{},*counter.lock().unwrap());}除了 MutexRust 还提供了 RwLock读写锁适用于读多写少的场景即多个线程可同时读取数据只有一个线程能写入数据能提升并发性能。进阶原子类型与 Send/Sync 特征原子类型无锁并发对于简单的共享变量如计数器使用 Mutex 会有一定的性能开销锁的获取和释放。Rust 标准库中的std::sync::atomic模块提供了原子类型如 AtomicUsize、AtomicBool无需加锁即可实现线程安全的读写操作性能更优。usestd::sync::Arc;usestd::sync::atomic::{AtomicUsize,Ordering};usestd::thread;fnmain(){// 创建原子计数器初始值为0letcounterArc::new(AtomicUsize::new(0));letmuthandlesvec![];for_in0..10{letcounterArc::clone(counter);lethandlethread::spawn(move||{// 原子递增使用 Relaxed 表示不保证内存顺序counter.fetch_add(1,Ordering::Relaxed);});handles.push(handle);}forhandleinhandles{handle.join().unwrap();}// 读取原子类型的值println!(原子计数器值{},counter.load(Ordering::Relaxed));}Send 与 SyncRust 的线程安全标记Rust 通过两个特殊的特征trait来标记类型的线程安全性编译器会自动检查确保线程间的数据访问安全Send表示类型的所有权可以安全地在线程间转移如String、Arc。Sync表示类型的引用可以安全地在多个线程间共享如MutexT、原子类型。大多数 Rust 标准类型如i32、VecT都自动实现了Send和Sync但也有例外RcT未实现Send和Sync只能用于单线程。裸指针*const T、*mut T未实现Send和Sync需手动保证安全。无需手动为类型实现Send和Sync编译器会自动推导只有当你实现自定义同步原语时才需要手动实现并确保其线程安全。总结Rust 的多线程设计看似复杂实则是为了安全与性能的平衡。只要掌握了所有权和同步原语的核心逻辑就能轻松编写高效、安全的多线程程序。后续可以深入学习异步编程进一步提升并发性能。