首页 / Rust 入门教程 / 并发安全原则小结

Rust 入门教程

并发安全原则小结

本教程共 78 篇 · 第 70 篇 · 更新于 2026-08-08 · 约 7 分钟阅读

RustRust 入门教程并发线程安全小结SendSync选型

本节目标:把第 65 到 69 章的并发知识串成一张网,给出”什么时候该用哪种并发方式”的选型指南,并提炼出几条守护并发安全的铁律,帮你在写并发代码时不踩坑。

从线程基础到 Send/Sync,我们走完了 Rust 并发的”操作系统线程”这条路。这一章不做新知识点,而是把前面内容缝起来,给你一份能带走的地图和几条守则。

1-1 五章回顾

先快速过一遍我们讲了什么:

  • 第 65 章 线程基础:用 thread::spawn 开线程,main 线程结束会带走所有子线程,用 join 等子线程,用 move 把数据所有权交过去。
  • 第 66 章 消息传递:用 mpsc 通道在线程间传数据,“用通信来共享内存”,消息按所有权规则转移,发送者全关接收循环才结束。
  • 第 67 章 共享状态并发:用 Mutex 互斥锁保护共享可变数据,加锁拿守卫、离开作用域自动解锁,配合 Condvar 做条件等待。
  • 第 68 章 Arc:用原子引用计数智能指针让多份引用共享同一份数据,Arc 只读,要改得配 Mutex
  • 第 69 章 Send / SyncSend 管所有权能否跨线程,Sync 管引用能否跨线程,是编译期并发安全的终极守门员。

这五章其实是”一条主线、两种风格”。主线是:Rust 用所有权 + 类型系统,把并发里最危险的”数据竞争”挡在编译期。两种风格是:消息传递(不共享内存)和共享状态(共享但加锁)。

1-2 怎么选并发模型

面对一个并发需求,先问自己两个问题:任务是什么类型?规模有多大?

用操作系统线程(本章内容)当:

  • 任务数量不多,比如几十个。
  • 任务是 CPU 密集型(大量计算),此时让线程数约等于 CPU 核心数,最能压榨硬件。
  • 你想要最简单直观的代码,线程内的代码就是普通顺序代码,几乎不用改写法。

用消息传递还是共享状态?

  • 线程间耦合松、数据流向清晰,优先消息传递(channel)。它的好处是数据流动方向明确,不容易出锁相关的问题。
  • 线程间要频繁读写同一份大状态(比如共享缓存、全局配置),用 Arc<Mutex<T>> 更直接。

什么时候该考虑 async(后面章节讲)?

  • 任务数量是”海量”,比如成千上万个网络连接同时等待。
  • 任务是 IO 密集型,大部分时间在等网络、等磁盘。
  • 这时纯多线程会因为线程太多而上下文切换爆炸,async/await 的 M:N 模型更省。

一句话记忆:少量任务、CPU 密集 → 线程;海量任务、IO 密集 → async; 这俩不是二选一,一个程序里经常混用。

1-3 守护并发安全的铁律

把前面零散的提醒收拢成几条,建议贴在显示器边上:

第一,不要依赖线程执行顺序。调度顺序由操作系统决定,每次运行可能都不同。代码逻辑绝不能建立”它一定先跑”的假设上。

第二,main 线程活着,子线程才有命。需要等子线程,记得 join;需要让子线程用外部数据,记得 move 转移所有权。

第三,共享可变状态必加锁。凡是多个线程要改同一份数据,Mutex/RwLock 不能省。读多写少可以上 RwLock,允许多个读者同时读。

第四,Arc 只管共享,不管改。想改共享数据,组合是 Arc<Mutex<T>>Arc<RwLock<T>>。单独 Arc 是只读视图。

第五,锁范围越小越好。只在真正访问共享数据的那几行加锁,别把无关计算也圈进去,否则别的线程被白白堵住。按固定顺序加锁,能避开死锁。

第六,编译器报错说”不能在线程间安全传递”,先别烦,那是 Send/Sync 在救你。它拦下的正是数据竞争。顺着报错把类型换成线程安全的版本(比如 RcArcRefCellMutex)通常就过了。

1-4 Rust 的”无畏并发”到底指什么

最后澄清一个常见误解。Rust 说并发是”无畏”(fearless)的,并不等于”并发不会出 bug”。死锁、逻辑错误、性能瓶颈,这些照样能写出来。Rust 保证的是:你不会写出数据竞争(data race)——那种多个线程无保护地同时访问同一内存、且至少有一个在写、导致结果不确定的最阴险 bug。

这是因为 Send/Sync、所有权、借用检查在编译期就筑起了墙。你写多线程代码时,很多在别的语言里要靠经验和测试去防的坑,Rust 编译器直接替你拦了。这就是”无畏”的真正含义:不是不会错,而是最坏那类内存安全问题被系统性地排除了。

1-5 一段选型小练习

光讲理论容易飘,来个小练习巩固。假设你要写一个程序:同时下载十个网页存盘。十这个数量不算海量,但每个下载都在等网络。该怎么选并发模型?

答案:用异步更合适。十个下载都卡在等 IO,线程模型下要开十个线程干等,浪费;异步下一个运行时就能并发伺候它们,谁的网络数据到了就处理谁。但如果你要的是”对一千万个数做并行求和”,那是 CPU 密集,线程数等于核心数最香,异步反而帮倒忙。这个练习体现的正是本章核心:看任务类型和规模,而不是无脑选一个。

再换个角度:如果这十个下载之间还要频繁改同一份共享状态,那就别用 async 硬刚,回到 Arc<Mutex<T>> 的线程模型更直观。模型之间没有绝对优劣,只有合不合适。

1-6 小结

五章并发内容到此收尾。核心就一句:Rust 用所有权和 Send/Sync 把数据竞争挡在编译期。实践中,少量 CPU 密集任务用线程,海量 IO 任务用 async;线程间要么传消息,要么用 Arc<Mutex<T>> 共享加锁。记住那六条铁律,并发代码就稳了一大半。接下来我们跨进另一个世界——异步编程。