← 中文资源

简体中文版本

互斥锁

互斥锁是保证同一时刻只有一个线程进入临界区的锁定原语,用于保护共享状态。

阅读英文原文

概念定义

互斥锁(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}");
}