编程后端开发者

Rust标准库提供了哪些线程同步方式?如何在它们之间进行选择,以及需要注意哪些“细微之处”?

用 Hintsage AI 助手通过面试

答案

Rust标准库提供了基本的同步原语,以安全地进行多线程操作:

  • Mutex — 为多个线程访问数据提供互斥;
  • RwLock — 允许多个读者(read)同时访问,但只有一个写者(write);
  • Condvar — 条件变量原语,用于根据事件唤醒线程;
  • Atomic类型AtomicBoolAtomicUsize等) — 无锁读取/写入操作,在硬件级别执行;
  • Arc(Atomic Reference Counted) — 具有线程安全性的引用计数,用于共享对象。

选择:

  • 如果只需要同时读取 — 使用 RwLock(比Mutex更高效)。
  • 对于单个线程的简单同步访问 — 使用 Mutex
  • 对于基于信号的同步(例如,“等待新元素”) — 使用 Condvar
  • 对于原子计数器/标志 — 使用 Atomic
  • 对于共享所有权(多线程) — 使用 Arc

示例:

use std::sync::{Arc, Mutex}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("结果: {}", *counter.lock().unwrap()); }

设问

问题: Rust是否保证使用 Mutex<T> 完全消除死锁风险,因为它在编译时进行了检查?

回答: 。Rust通过所有权和借用检查器提供安全的数据访问,但在语言层面不防止死锁的发生。死锁纯粹是由于多个Mutex的捕获顺序被打乱或递归捕获而在逻辑上发生的。示例:

use std::sync::Mutex; let lock1 = Mutex::new(0); let lock2 = Mutex::new(0); // 线程1: lock1 -> lock2, 线程2: lock2 -> lock1 ⇒ 死锁

历史

数据流处理项目经历了偶发的“暂停”情况。结果发现开发者在没有严格捕获顺序的情况下使用了嵌套的Mutex(Mutex内部包含Mutex),导致了相互阻塞(死锁),只能通过强制终止进程来解除。

历史

在一个大型服务中,大规模迁移从 Mutex<Option<T>>RwLock<T>,没有考虑到可写锁的时间可能比读锁更长。在高峰负载下,这导致了多达数十秒的处理延迟,因为在写入上有排队现象。

历史

程序员试图在线程上节省开支,“推送” Arc<Mutex<_>> 到数百个线程。由于调度程序的细微之处和mutex的重用,出现了奇怪的相互等待 — 性能降低了5倍!