文章目录
-
-
- 一、同步机制基本概念
- [二、互斥锁(Mutex)------ 独占访问](#二、互斥锁(Mutex)—— 独占访问)
- [三、读写锁(RwLock)------ 多读单写](#三、读写锁(RwLock)—— 多读单写)
- [四、条件变量(Condvar)------ 等待通知](#四、条件变量(Condvar)—— 等待通知)
- [五、屏障(Barrier)------ 多线程同步点](#五、屏障(Barrier)—— 多线程同步点)
- [六、mpsc::channel ------ 消息传递](#六、mpsc::channel —— 消息传递)
- [七、Once / OnceCell / OnceLock ------ 一次性初始化](#七、Once / OnceCell / OnceLock —— 一次性初始化)
- [本章小结 + 进阶练习](#本章小结 + 进阶练习)
-
摘要 :Rust 同步机制以"消除数据竞争、保证临界区串行化"为核心。Mutex<T> 提供互斥访问(RAII 自动解锁、!Send 单线程 Guard);RwLock<T> 支持多读单写;Condvar + Mutex 实现条件等待(必须 while 循环防虚假唤醒);Barrier 同步多线程到达点(is_leader 区分主线程);mpsc::channel 分异步(无界链表)与同步(有界缓冲)两种;Once / OnceCell / OnceLock 解决多线程全局变量一次性初始化。本文深度解析 RAII、!Send 设计、虚假唤醒、通道特性及 OnceLock 静态安全,帮助你写出高效、无死锁、无数据竞争的并发代码,真正掌握 Rust "同步即安全"的工程实践。(158 字)
一、同步机制基本概念
专业名词释义:
- 并发(Concurrent):多个任务"同时存在"(分时复用,单核也支持)。
- 并行(Parallel):多个任务"同时执行"(多核独占)。
- 竞态条件(Race Condition):事件时序不可控(不一定出错)。
- 数据竞争(Data Race):同一数据读写并行(Rust 借用规则彻底消除)。
- 临界区(Critical Section):共享数据的读写代码片段。
- 数据同步(Data Synchronization):目标------临界区串行化;手段------锁、条件变量、屏障、通道、原子操作。
注意事项与最佳实践:
- 单核只有并发,多核并发+并行同时存在。
- 深度提示:Rust 借用检查在编译期消灭数据竞争,运行时锁只防业务竞态。
- 最佳实践:优先通道(无共享内存)、再锁(最小临界区)、最后原子(性能极致)。
二、互斥锁(Mutex)------ 独占访问
专业名词释义:
- Mutex :互斥锁,保护共享数据,
lock()返回MutexGuard(RAII 自动解锁)。 - !Send:Guard 禁止跨线程传递(防止死锁)。
用法示例:
rust
use std::sync::{Arc, Mutex};
use std::thread;
let val = Arc::new(Mutex::new(100));
let cloned = val.clone();
let h1 = thread::spawn(move || {
println!("thr1 {}", val.lock().unwrap());
});
let h2 = thread::spawn(move || {
println!("thr2 {}", cloned.lock().unwrap());
});
h1.join().unwrap(); h2.join().unwrap();
静态全局:
rust
static LOCK: Mutex<i32> = Mutex::new(100);
// 同上 spawn 使用
注意事项与最佳实践:
- RAII +
DerefMut自动解锁,无需手动 unlock。 - 深度提示:重复加锁行为未定义(死锁或 panic)。
- 最佳实践 :共享数据永远
Arc<Mutex<T>>;临界区越小越好;静态用lazy_static或OnceLock。
三、读写锁(RwLock)------ 多读单写
专业名词释义:
- RwLock :读锁可并行,写锁互斥(
read()/write()返回RwLockReadGuard/RwLockWriteGuard)。
用法示例:
rust
use std::sync::{Arc, RwLock};
let val = Arc::new(RwLock::new(100));
let cloned = val.clone();
let h1 = thread::spawn(move || {
println!("thr1 {}", val.read().unwrap());
});
let h2 = thread::spawn(move || {
println!("thr2 {}", cloned.write().unwrap());
});
全局资源 (Box::leak):
rust
let boxed = Box::leak(Box::new(RwLock::new(100)));
let val = &*boxed;
// 同上 spawn
注意事项与最佳实践:
- 读锁可多线程并行,写锁独占(Rust 借用规则保证)。
- 深度提示:重复加锁(读或写)都会死锁/panic。
- 最佳实践 :读多写少场景首选
RwLock;写锁持有时间越短越好。
四、条件变量(Condvar)------ 等待通知
专业名词释义:
- Condvar :与
Mutex配合,wait释放锁等待,notify_one/all唤醒。
高效写法 (必须 while):
rust
let mut guard = mutex.lock().unwrap();
while !guard.condition() {
guard = cvar.wait(guard).unwrap();
}
guard.do_something();
虚假唤醒(Spurious Wakeup):
wait可能被系统信号虚假唤醒 → 必须while循环重新检查条件。
注意事项与最佳实践:
- 临界区(条件判断+设置)永远在
Mutex保护内。 - 深度提示 :
wait先释放锁、再加锁,中间条件可能变化。 - 最佳实践 :生产者
notify_all,消费者while !cond;业务需区分notify_one/notify_all。
五、屏障(Barrier)------ 多线程同步点
专业名词释义:
- Barrier :所有线程到达
wait()才集体放行(is_leader区分主线程)。
用法示例:
rust
use std::sync::{Arc, Barrier};
let barrier = Arc::new(Barrier::new(10));
for _ in 0..10 {
let c = Arc::clone(&barrier);
thread::spawn(move || {
println!("before wait");
c.wait();
println!("after wait");
});
}
注意事项与最佳实践:
BarrierWaitResult::is_leader()仅一个线程返回 true。- 深度提示 :返回类型
Send,可跨线程传递(与 MutexGuard 不同)。 - 最佳实践 :并行计算"重新对时"场景;结合
Condvar可手动模拟。
六、mpsc::channel ------ 消息传递
专业名词释义:
- 异步 channel (
channel()):无界链表,发送不阻塞。 - 同步 channel (
sync_channel(bound)):有界缓冲,发送可能阻塞。
异步示例:
rust
let (tx, rx) = mpsc::channel();
thread::spawn(move || { tx.send("hi".to_string()).unwrap(); });
let received = rx.recv().unwrap();
同步队列特点:
- 缓冲区满时
send阻塞;提供try_send非阻塞。
注意事项与最佳实践:
- 所有发送者 drop 后,接收端
recv返回Err(优雅关闭)。 - 深度提示:异步用链表,同步用预分配数组。
- 最佳实践:线程间通信首选 channel(无锁、无共享内存);生产者-消费者模式用同步队列控流。
七、Once / OnceCell / OnceLock ------ 一次性初始化
专业名词释义:
- Once :多线程安全全局初始化(
call_once只执行一次)。 - OnceCell :单线程版,只能写一次(
get_or_init)。 - OnceLock :线程安全版,可用于
static。
Once 示例:
rust
static mut VAL: usize = 0;
static INIT: Once = Once::new();
fn get_cached_val() -> usize {
unsafe {
INIT.call_once(|| { VAL = expensive_computation(); });
VAL
}
}
OnceCell / OnceLock:
rust
// OnceCell(单线程)
let cell = OnceCell::new();
let value = cell.get_or_init(|| "Hello".to_string());
// OnceLock(多线程 static)
static CELL: OnceLock<String> = OnceLock::new();
let value = CELL.get_or_init(|| "Hello".to_string());
注意事项与最佳实践:
- 消除多线程初始化竞态(同
pthread_once)。 - 深度提示 :
OnceCell::take后可再次写入;OnceLock完美替代lazy_static。 - 最佳实践 :单例模式、配置加载、缓存初始化全用
OnceLock。
本章小结 + 进阶练习
学完本章你应该能做到:
- 熟练使用
Mutex/RwLock+ RAII +!Send保证安全 - 掌握
Condvarwhile防虚假唤醒、Barrier同步点、mpsc通道 - 灵活运用
OnceLock实现线程安全全局初始化
进阶练习(建议立刻敲代码):
- 用
Arc<RwLock<HashMap<K,V>>>实现并发缓存(读多写少)。 - 用
Condvar+Mutex实现生产者-消费者(缓冲区满/空等待)。 - 用
Barrier同步 8 个线程并行计算后汇总结果。 - 用
mpsc::sync_channel(0)实现"同步握手"线程间信号。 - 用
OnceLock实现线程安全的全局配置单例(支持懒加载)。 - 对比
MutexvsRwLock在 100 读 1 写场景的性能(benchmark)。
Mutex + RwLock + Condvar + Channel + OnceLock = Rust 同步全家桶 。掌握 RAII 与 !Send 设计,你就能 fearless 地构建高并发系统,真正实现"编译期安全 + 运行时高效"!
(完)