Files
Operating-System/Thread/mutual_exclusion.md
2026-03-22 17:40:44 +08:00

244 lines
4.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
线程互斥本质上是在解决一个问题:**多个线程同时访问共享资源时,如何保证“同一时刻只有一个线程在操作”**。不同方案的差别主要在于:是否进入内核、是否忙等、性能如何、适用场景。
我按“从底层到高级”的逻辑给你梳一遍,这样你能建立一条完整的认知链。
---
## 1. 关闭中断(最底层,仅限内核)
**思路:**
在临界区执行前关闭中断,执行完再打开。
**效果:**
* 当前 CPU 不会被打断
* 不会发生线程切换 → 自然不会并发
**问题:**
* 只对单核有效(多核直接失效)
* 用户态做不了
* 会影响系统响应(很危险)
**结论:**
这是操作系统内核里的“原始手段”,你可以理解为最暴力的锁。
---
## 2. 原子操作Atomic / CAS
**核心:**
CPU 提供的不可分割操作,比如:
* Test-and-Set
* Compare-and-SwapCAS
**典型代码逻辑:**
```c
while (CAS(lock, 0, 1) == false) {
// busy wait
}
```
**特点:**
* 不进入内核(用户态)
* 极快
* 但会**忙等spin**
---
## 3. 自旋锁Spinlock
其实就是“原子操作 + 死循环”:
```c
while (lock == 1); // 一直等
lock = 1;
```
实际实现用 CAS 保证安全。
**特点:**
* 不睡眠,一直占 CPU
* 适合:
* 临界区很短
* 多核环境
**问题:**
* 临界区长 → CPU白烧
* 单核上很蠢(等自己)
---
## 4. 互斥锁Mutex
这是你最常用的。
**核心思想:**
拿不到锁 → 线程**睡眠(阻塞)**
**流程:**
1. 尝试获取锁CAS
2. 失败 → 进入内核
3. 被挂起(加入等待队列)
4. 被唤醒再竞争
**特点:**
* 不浪费 CPU
* 需要系统调用(慢一点)
**一句话理解:**
> 自旋锁是“在门口死等”Mutex 是“拿号排队等叫号”。
---
## 5. 读写锁Read-Write Lock
**优化场景:读多写少**
规则:
* 多个读可以同时进行
* 写必须独占
**效果:**
* 提升并发性能
**问题:**
* 写可能被饿死(读太多)
---
## 6. 信号量Semaphore
本质是一个**计数器**。
### 两种:
* 二值信号量(≈ Mutex
* 计数信号量(允许 N 个线程进入)
**经典操作:**
* Pwait减1不够就阻塞
* Vsignal加1唤醒别人
**适合:**
* 资源池(比如连接池)
---
## 7. 条件变量Condition Variable
不是锁,是**配合 Mutex 用的**
**用途:**
线程等待某个条件成立
```c
pthread_cond_wait(cond, mutex);
```
**特点:**
* 自动释放 mutex
* 被唤醒后重新加锁
**典型场景:**
生产者-消费者
---
## 8. 乐观锁(无锁编程)
**核心思想:**
> 不加锁,赌冲突很少
用 CAS + 重试:
```c
do {
old = x;
new = old + 1;
} while (!CAS(&x, old, new));
```
**特点:**
* 无锁lock-free
* 高性能
* 代码复杂,容易错
---
## 9. FutexLinux关键机制
你前面学内核,这个必须知道。
**全称:**
Fast Userspace Mutex
**核心思想:**
* 优先在用户态自旋
* 实在不行再进入内核睡眠
**流程:**
1. CAS尝试拿锁
2. 失败 → futex_wait进内核
3. 解锁 → futex_wake
**一句话:**
> Mutex = 自旋锁 + 睡眠futex实现
---
## 总结一张图(抽象层级)
从低到高:
```
关闭中断(内核)
原子操作CAS
自旋锁(忙等)
Mutex阻塞
读写锁 / 信号量 / 条件变量(高级同步)
无锁编程CAS体系
```
---
## 给你一个“工程选型直觉”
你以后写程序可以这样选:
* 临界区极短 → 自旋锁
* 普通情况 → Mutex
* 读多写少 → 读写锁
* 资源数量控制 → 信号量
* 线程协作 → 条件变量
* 极致性能 → CAS / 无锁
---
如果你想更进一步,我可以帮你把这些**全部映射到 Linux 内核实现futex、task_struct、调度器**,甚至给你画出“线程阻塞时内核到底干了什么”的完整路径,那一块才是真正把知识打通的地方。