305 lines
4.7 KiB
Markdown
305 lines
4.7 KiB
Markdown
哈哈,**好,这一波咱把 `<mutex>` 彻底端了** 😄
|
||
你现在这个节奏是对的:**thread 看清 → mutex 看透 → 并发就不怕了**。
|
||
|
||
我给你的是一份 **能当“并发速查表”的 `<mutex>` 全家桶说明**,照着用不翻车。
|
||
|
||
---
|
||
|
||
# 一、`<mutex>` 是干嘛的?一句话版
|
||
|
||
> **`<mutex>` 负责“同步”,不负责“并发”。**
|
||
|
||
* `thread`:开人
|
||
* `mutex`:防抢
|
||
* `atomic`:小件快递
|
||
* `future`:结果回收
|
||
|
||
---
|
||
|
||
# 二、`std::mutex`(最常用)
|
||
|
||
## 1️⃣ `std::mutex`
|
||
|
||
```cpp
|
||
#include <mutex>
|
||
|
||
std::mutex m;
|
||
```
|
||
|
||
### 作用
|
||
|
||
* 最普通的互斥锁
|
||
* **同一时间只能一个线程进入临界区**
|
||
|
||
---
|
||
|
||
## 2️⃣ `lock()` —— 上锁(阻塞)
|
||
|
||
```cpp
|
||
m.lock();
|
||
// 临界区
|
||
m.unlock();
|
||
```
|
||
|
||
### 特点
|
||
|
||
* 如果锁被占用 → **阻塞等待**
|
||
* 不可递归
|
||
|
||
### 返回值
|
||
|
||
❌ 无
|
||
|
||
### ❗新手警告
|
||
|
||
> **一旦忘记 `unlock()`,程序直接死锁**
|
||
|
||
---
|
||
|
||
## 3️⃣ `unlock()` —— 解锁
|
||
|
||
```cpp
|
||
m.unlock();
|
||
```
|
||
|
||
### 返回值
|
||
|
||
❌ 无
|
||
|
||
---
|
||
|
||
## 4️⃣ `try_lock()` —— 尝试上锁(不阻塞)
|
||
|
||
```cpp
|
||
if (m.try_lock()) {
|
||
// 拿到锁
|
||
m.unlock();
|
||
}
|
||
```
|
||
|
||
### 返回值
|
||
|
||
* `true`:成功
|
||
* `false`:失败
|
||
|
||
### 用途
|
||
|
||
* 避免卡死
|
||
* 非关键任务
|
||
|
||
---
|
||
|
||
# 三、RAII 锁(**你真正该用的**)
|
||
|
||
> **99% 的情况下,不要直接用 `lock()/unlock()`**
|
||
|
||
---
|
||
|
||
## 5️⃣ `std::lock_guard`(最常用、最安全)
|
||
|
||
```cpp
|
||
void foo(){
|
||
std::lock_guard<std::mutex> lock(m);
|
||
// 自动上锁
|
||
// 自动解锁(作用域结束)
|
||
}
|
||
```
|
||
|
||
### 特点
|
||
|
||
* 构造时上锁
|
||
* 析构时解锁
|
||
* **不能手动 unlock**
|
||
|
||
### 返回值
|
||
|
||
❌ 无
|
||
|
||
✔ 最安全
|
||
✔ 最推荐
|
||
|
||
---
|
||
|
||
## 6️⃣ `std::unique_lock`(高级版)
|
||
|
||
```cpp
|
||
std::unique_lock<std::mutex> lock(m);
|
||
```
|
||
|
||
### 特点
|
||
|
||
* 可以:
|
||
|
||
* 延迟上锁
|
||
* 手动 unlock / lock
|
||
* 搭配 `condition_variable`
|
||
|
||
### 常见用法
|
||
|
||
```cpp
|
||
std::unique_lock<std::mutex> lock(m);
|
||
lock.unlock();
|
||
// ...
|
||
lock.lock();
|
||
```
|
||
|
||
### 返回值
|
||
|
||
❌ 无
|
||
|
||
📌 **灵活 ≠ 常用**,新手少用
|
||
|
||
---
|
||
|
||
## 7️⃣ 延迟上锁
|
||
|
||
```cpp
|
||
std::unique_lock<std::mutex> lock(m, std::defer_lock);
|
||
// 之后再 lock()
|
||
lock.lock();
|
||
```
|
||
|
||
---
|
||
|
||
## 8️⃣ 尝试上锁(不阻塞)
|
||
|
||
```cpp
|
||
std::unique_lock<std::mutex> lock(m, std::try_to_lock);
|
||
if (lock.owns_lock()) {
|
||
// 成功
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# 四、特殊 mutex 类型
|
||
|
||
## 9️⃣ `std::recursive_mutex`
|
||
|
||
```cpp
|
||
std::recursive_mutex m;
|
||
```
|
||
|
||
### 特点
|
||
|
||
* 同一线程可以多次 lock
|
||
* 必须 unlock 相同次数
|
||
|
||
### 什么时候用?
|
||
|
||
* 递归函数
|
||
* 老代码
|
||
|
||
⚠️ **能不用就不用**,通常是设计有问题
|
||
|
||
---
|
||
|
||
## 🔟 `std::timed_mutex`
|
||
|
||
```cpp
|
||
std::timed_mutex m;
|
||
```
|
||
|
||
### 作用
|
||
|
||
* 支持超时锁
|
||
|
||
```cpp
|
||
if (m.try_lock_for(100ms)) {
|
||
m.unlock();
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 1️⃣1️⃣ `std::recursive_timed_mutex`
|
||
|
||
= `recursive_mutex` + 超时
|
||
|
||
---
|
||
|
||
# 五、一次锁多个 mutex(死锁克星)
|
||
|
||
## 1️⃣2️⃣ `std::lock()`
|
||
|
||
```cpp
|
||
std::lock(m1, m2);
|
||
// 全部锁成功
|
||
```
|
||
|
||
### 特点
|
||
|
||
* 避免死锁
|
||
* 要么全拿,要么全不拿
|
||
|
||
---
|
||
|
||
## 1️⃣3️⃣ `std::scoped_lock`(C++17)
|
||
|
||
```cpp
|
||
std::scoped_lock lock(m1, m2);
|
||
```
|
||
|
||
✔ 自动
|
||
✔ 安全
|
||
✔ 推荐
|
||
|
||
---
|
||
|
||
# 六、完整 API 总表(收藏级)
|
||
|
||
| API | 作用 | 返回值 |
|
||
| ----------------------- | ----------- | ---- |
|
||
| `mutex` | 基础互斥锁 | — |
|
||
| `lock()` | 阻塞上锁 | void |
|
||
| `unlock()` | 解锁 | void |
|
||
| `try_lock()` | 尝试上锁 | bool |
|
||
| `recursive_mutex` | 可递归锁 | — |
|
||
| `timed_mutex` | 超时锁 | — |
|
||
| `recursive_timed_mutex` | 递归+超时 | — |
|
||
| `lock_guard` | RAII 锁 | — |
|
||
| `unique_lock` | 高级 RAII 锁 | — |
|
||
| `scoped_lock` | 多锁 RAII | — |
|
||
| `lock()` | 同时锁多个 mutex | void |
|
||
|
||
---
|
||
|
||
# 七、什么时候用 mutex?一句判断法
|
||
|
||
> **如果一个变量:**
|
||
>
|
||
> * 被多个线程访问
|
||
> * 至少一个线程写
|
||
|
||
👉 **要么 mutex,要么 atomic**
|
||
|
||
---
|
||
|
||
# 八、什么时候不要用 mutex?
|
||
|
||
❌ 只是计数
|
||
❌ 简单 flag
|
||
❌ 单一整数状态
|
||
|
||
👉 用 `atomic`,更快更简单。
|
||
|
||
---
|
||
|
||
# 九、新手不翻车口诀(记住这 4 句)
|
||
|
||
1️⃣ **能不共享就不共享**
|
||
2️⃣ **能分任务就别加锁**
|
||
3️⃣ **能用 `lock_guard` 就别手写 lock**
|
||
4️⃣ **看到 `recursive_mutex` 先怀疑设计**
|
||
|
||
---
|
||
|
||
你现在已经把 **thread + mutex** 两大核心吃下来了。
|
||
下一步顺序我强烈建议:
|
||
|
||
👉 **`<atomic>`(最后一块拼图)**
|
||
或者
|
||
👉 **`condition_variable`(线程协作)**
|
||
|
||
你选一个,我继续给你“端锅”。
|