概念定义
互斥锁(mutex)是一个只有两种状态的锁。取得它的线程可以进入受保护区域;在它被持有期间尝试获取的其他线程会阻塞或失败,取决于 API。释放后等待者得以继续。其保证狭窄而精确:同一时刻至多一个持有者。其余一切——公平性、顺序、崩溃后的行为——都取决于实现与调用方的纪律。
为什么重要
没有锁的共享可变状态本身就是竞态条件。互斥锁是让读-改-写序列相对于其他线程保持原子的最小工具。在进程内,它保护计数器与缓存;跨进程时,文件锁或数据库锁把同一思想扩展到更宽的边界。几乎所有更高层的构件——线程池、actor 邮箱、连接池——内部都用到它。
工程实践
明确该锁所保护的不变量,确保对该状态的每一次访问都取同一把锁。保持临界区短小且不含 I/O,并按一致的全局顺序取锁以避免死锁。优先使用能自动释放的作用域守卫,使任何退出路径都不会泄漏锁,并思考原子操作或不可变设计是否能彻底消除对锁的需求。
代码示例
use arc_swap::ArcSwap;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
struct Config {
max_connections: usize,
feature_flag: bool,
}
struct ConfigStore {
current: ArcSwap<Config>,
}
impl ConfigStore {
fn new(initial: Config) -> Self {
Self {
current: ArcSwap::from_pointee(initial),
}
}
fn load(&self) -> Arc<Config> {
self.current.load_full()
}
fn reload(&self, next: Config) -> Arc<Config> {
self.current.swap(Arc::new(next))
}
}
fn main() {
let store = ConfigStore::new(Config {
max_connections: 8,
feature_flag: false,
});
let snapshot = store.load();
let previous = store.reload(Config {
max_connections: 16,
feature_flag: true,
});
println!("previous: {:?}", previous);
println!("snapshot: {:?}", snapshot);
println!("current: {:?}", store.load());
assert_eq!(store.load().max_connections, 16);
}
use std::sync::mpsc;
use std::thread;
fn main() {
let (sender, receiver) = mpsc::channel::<u32>();
let producers: Vec<_> = (0..3)
.map(|id| {
let sender = sender.clone();
thread::spawn(move || {
for item in 1..=3 {
sender.send(id * 10 + item).expect("send failed");
}
})
})
.collect();
drop(sender);
let consumer = thread::spawn(move || {
let mut total = 0u32;
while let Ok(value) = receiver.recv() {
total += value;
}
total
});
for handle in producers {
handle.join().expect("producer panicked");
}
let total = consumer.join().expect("consumer panicked");
println!("total = {total}");
}