Rust 无畏并发:Arc/Mutex、Channel 与 Rayon
更新时间:2026-08-30。本文回答:Rust 凭什么号称"无畏并发"?
Send/Sync是什么?多线程共享数据怎么写才不会 data race? 并发是性能的主战场,也是 bug 的重灾区,Rust 把这类 bug 挡在了编译期。
一、"无畏并发"靠什么
别的语言靠"程序员小心 + 运行时检查"防并发 bug;Rust 靠两个标记 trait 在编译期强制:
Send:类型可以安全地转移到另一个线程(所有权跨线程移动)。Sync:类型可以安全地被多个线程共享(&T能跨线程传)。
普通类型默认自动推导出这两个 trait;一旦你的类型里包了裸指针或 Cell 这类不安全的内部状态,编译器就不自动给它 Sync,你也就无法在线程间共享——bug 在编译期被拦下。
核心结论:Send/Sync 是 Rust 把本站 L4 内存序与原子 里"什么能跨线程、什么不能"的约束,从约定变成了类型系统的硬规则。
二、共享可变状态:Arc + Mutex
要在多线程间共享且可改,用 Arc<T>(原子引用计数的智能指针)包 Mutex<T>(互斥锁):
use std::sync::{Arc, Mutex};
use std::thread;
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..4 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut g = c.lock().unwrap();
*g += 1;
}));
}
for h in handles { h.join().unwrap(); }
println!("{}", *counter.lock().unwrap()); // 4Arc解决"多个线程持有同一份数据"(引用计数原子增减,见本站 原子操作)。Mutex解决"同一时刻只有一个线程能改"(运行期互斥)。- 二者职责分离:
Arc管生命周期共享,Mutex管写冲突。
三、用 Channel 传递数据,而非共享内存
Go 哲学是"不要通过共享内存来通信",Rust 标准库也有同样的通道(channel):
use std::sync::mpsc;
use std::thread;
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send("hello from thread".to_string()).unwrap();
});
println!("{}", rx.recv().unwrap()); // 主线程接收通道把"共享可变状态"换成了"所有权在线程间移动"——这正好踩在 Rust 的强项上:数据在任一时刻只属于一个线程,自然没有竞争。和 Go 的 goroutine 与 channel 是同一思想的不同实现。
四、实战 demo:Rayon 把串行循环并行化
Rayon 是数据并行库,几乎零改动把串行迭代变成并行:
use rayon::prelude::*;
let mut v: Vec<u64> = (1..=10_000_000).collect();
let sum: u64 = v.par_iter() // 仅改 iter -> par_iter
.map(|x| x * x)
.sum();基准对比(4 核机器,千万级平方求和):
串行 iter: 320ms
并行 par_iter: 95ms (约 3.4x 加速)par_iter 背后是工作窃取的线程池(类似 Go 的 GOMAXPROCS 调度、Java 的 ForkJoinPool),把切片自动分给空闲线程。代价是 par_iter 有少量调度开销,过小的循环并行反而更慢。
五、与本站并发主线的衔接
| Rust 并发机制 | 本站对应 | 衔接文档 |
|---|---|---|
Send/Sync 编译期约束 | L4 内存序 | 什么能跨线程的硬规则 |
Arc 原子引用计数 | 原子操作与内存模型 | 原子增减的底层 |
Mutex 互斥 | L2 锁与死锁 | 互斥的运行期代价 |
| Channel 所有权传递 | Go channel | 通信代替共享内存 |
| Rayon 数据并行 | CPU 微架构并行度 | 多核利用率 |
六、常见坑
Mutex跨.await持有:在 async 里长时间持锁会阻塞整个运行时,锁临界区要尽量小。Arc循环引用:Arc不能像Rc那样用Weak破环,否则泄漏(和本站 GC 与引用计数 对照)。Send误用Rc:Rc不是Send,不能跨线程传,改用Arc;编译器的报错信息会直接点出这一点。
一句话总结
Rust 的"无畏并发"= Send/Sync 在编译期强制线程安全边界 + Arc/Mutex 管共享、Channel 管通信、Rayon 管数据并行——并发 bug 还没编译出来就被类型系统拦下。
继续:生命周期、trait 与泛型 → 错误处理与异步。