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