摘要: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 禁止跨线程传递(防止死锁)。

用法示例

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();

静态全局

static LOCK: Mutex<i32> = Mutex::new(100);
// 同上 spawn 使用

注意事项与最佳实践

  • RAII + DerefMut 自动解锁,无需手动 unlock。
  • 深度提示:重复加锁行为未定义(死锁或 panic)。
  • 最佳实践:共享数据永远 Arc<Mutex<T>>;临界区越小越好;静态用 lazy_staticOnceLock

三、读写锁(RwLock)—— 多读单写

专业名词释义

  • RwLock:读锁可并行,写锁互斥(read() / write() 返回 RwLockReadGuard / RwLockWriteGuard)。

用法示例

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):

let boxed = Box::leak(Box::new(RwLock::new(100)));
let val = &*boxed;
// 同上 spawn

注意事项与最佳实践

  • 读锁可多线程并行,写锁独占(Rust 借用规则保证)。
  • 深度提示:重复加锁(读或写)都会死锁/panic。
  • 最佳实践:读多写少场景首选 RwLock;写锁持有时间越短越好。

四、条件变量(Condvar)—— 等待通知

专业名词释义

  • Condvar:与 Mutex 配合,wait 释放锁等待,notify_one/all 唤醒。

高效写法(必须 while):

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 区分主线程)。

用法示例

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 —— 消息传递

专业名词释义

  • 异步 channelchannel()):无界链表,发送不阻塞。
  • 同步 channelsync_channel(bound)):有界缓冲,发送可能阻塞。

异步示例

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 示例

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

// 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 保证安全
  • 掌握 Condvar while 防虚假唤醒、 Barrier 同步点、 mpsc 通道
  • 灵活运用 OnceLock 实现线程安全全局初始化

进阶练习(建议立刻敲代码):

  1. Arc<RwLock<HashMap<K,V>>> 实现并发缓存(读多写少)。
  2. Condvar + Mutex 实现生产者-消费者(缓冲区满/空等待)。
  3. Barrier 同步 8 个线程并行计算后汇总结果。
  4. mpsc::sync_channel(0) 实现“同步握手”线程间信号。
  5. OnceLock 实现线程安全的全局配置单例(支持懒加载)。
  6. 对比 Mutex vs RwLock 在 100 读 1 写场景的性能(benchmark)。

Mutex + RwLock + Condvar + Channel + OnceLock = Rust 同步全家桶。掌握 RAII 与 !Send 设计,你就能 fearless 地构建高并发系统,真正实现“编译期安全 + 运行时高效”!

(完)

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐