Thread Learning
This commit is contained in:
190
Thread/Async-Future.md
Normal file
190
Thread/Async-Future.md
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
哈哈,好的,你现在已经把 **线程、互斥、原子**都啃完了,剩下的就是 **“有返回值的并发”**——也就是 `<future>` / `<async>` 的领域了。
|
||||||
|
我给你整理一份 **速查表+说明**,保证看完就能用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 一、`std::async` 基本概念
|
||||||
|
|
||||||
|
> **作用**:开启一个任务,并且自动管理线程,可以**拿返回值**。
|
||||||
|
> 它本质是“带未来值的线程”。
|
||||||
|
|
||||||
|
### 1️⃣ 基本用法
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <future>
|
||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int work(int x) { return x * x; }
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
future<int> f = async(work, 5);
|
||||||
|
int result = f.get(); // 阻塞直到返回值就绪
|
||||||
|
cout << result << endl; // 25
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 参数详解
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
async(launch::policy policy, Callable f, Args... args)
|
||||||
|
```
|
||||||
|
|
||||||
|
* `policy` 可选:
|
||||||
|
|
||||||
|
* `launch::async` → 必在新线程运行
|
||||||
|
* `launch::deferred` → 等到调用 `get()` 才执行(懒加载)
|
||||||
|
* 可以用 `|` 混合
|
||||||
|
* `f` → 函数或 lambda
|
||||||
|
* `args...` → 参数
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
* `std::future<R>`
|
||||||
|
* `get()` 得到实际返回值
|
||||||
|
* 阻塞直到任务完成
|
||||||
|
* 调用一次 `get()` 后 future 失效
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 二、`std::future` 基本操作
|
||||||
|
|
||||||
|
| 函数 | 作用 | 返回值 |
|
||||||
|
| ------------------------ | -------- | --------------- |
|
||||||
|
| `get()` | 阻塞等待结果 | `R` |
|
||||||
|
| `wait()` | 阻塞直到准备好 | void |
|
||||||
|
| `wait_for(duration)` | 阻塞指定时间 | `future_status` |
|
||||||
|
| `wait_until(time_point)` | 阻塞到指定时间点 | `future_status` |
|
||||||
|
| `valid()` | 是否有有效任务 | bool |
|
||||||
|
|
||||||
|
`future_status` 枚举:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::future_status::ready // 已就绪
|
||||||
|
std::future_status::timeout // 超时
|
||||||
|
std::future_status::deferred // 延迟执行
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 三、`std::promise`(future 的搭档)
|
||||||
|
|
||||||
|
> **作用**:手动给 future 设置值
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
std::promise<int> p;
|
||||||
|
std::future<int> f = p.get_future();
|
||||||
|
|
||||||
|
// 子线程
|
||||||
|
std::thread([&p](){
|
||||||
|
p.set_value(42);
|
||||||
|
}).detach();
|
||||||
|
|
||||||
|
// 主线程
|
||||||
|
int val = f.get(); // 42
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关键点
|
||||||
|
|
||||||
|
* `set_value()` → 给 future 赋值
|
||||||
|
* `set_exception()` → 把异常传给 future
|
||||||
|
* `get_future()` → 得到与 promise 绑定的 future
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 四、组合 async + future + lambda(最常用)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto f = async([](int x){ return x*2; }, 10);
|
||||||
|
cout << f.get(); // 20
|
||||||
|
```
|
||||||
|
|
||||||
|
* 不用手动管理线程
|
||||||
|
* 可以拿返回值
|
||||||
|
* 自动 join / 管理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 五、等待与超时(高级)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
future<int> f = async(work, 5);
|
||||||
|
if(f.wait_for(500ms) == future_status::ready) {
|
||||||
|
cout << f.get() << endl;
|
||||||
|
} else {
|
||||||
|
cout << "还没完成" << endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* 非阻塞方式获取结果
|
||||||
|
* 用于并行计算 + 超时控制
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 六、`future` 全家桶速查表
|
||||||
|
|
||||||
|
| 类型 / 函数 | 作用 | 返回值 |
|
||||||
|
| ------------------------ | ------------- | ------------- |
|
||||||
|
| `std::future<T>` | 保存异步任务结果 | — |
|
||||||
|
| `get()` | 阻塞获取结果 | `T` |
|
||||||
|
| `wait()` | 阻塞直到就绪 | void |
|
||||||
|
| `wait_for(duration)` | 阻塞指定时间 | future_status |
|
||||||
|
| `wait_until(time_point)` | 阻塞到指定时间点 | future_status |
|
||||||
|
| `valid()` | 是否有效 | bool |
|
||||||
|
| `std::promise<T>` | 手动设置 future 值 | — |
|
||||||
|
| `set_value()` | 设置值 | void |
|
||||||
|
| `set_exception()` | 设置异常 | void |
|
||||||
|
| `get_future()` | 获取 future | future<T> |
|
||||||
|
| `std::async()` | 开启任务 | future<T> |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 七、使用建议
|
||||||
|
|
||||||
|
1️⃣ **如果需要返回值 → 用 async 或 promise/future**
|
||||||
|
2️⃣ **不用 async 就直接 thread + mutex/atomic → 仅做“干活”**
|
||||||
|
3️⃣ **async + lambda 最简洁**,避免手动 join / detach
|
||||||
|
4️⃣ **多线程大计算 → future + async + wait_for**,保证不会死锁
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 八、示例:多线程计算 + 获取返回值
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <future>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int work(int x){ return x*x; }
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
vector<future<int>> tasks;
|
||||||
|
for(int i=1; i<=4; i++){
|
||||||
|
tasks.push_back(async(work, i));
|
||||||
|
}
|
||||||
|
for(auto &f: tasks){
|
||||||
|
cout << f.get() << " ";
|
||||||
|
}
|
||||||
|
cout << endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:`1 4 9 16`(顺序可能一样也可能不同)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
总结一句话:
|
||||||
|
|
||||||
|
> **thread → 干活**
|
||||||
|
> **mutex/atomic → 防抢/快抢**
|
||||||
|
> **future/async → 干活 + 拿结果**
|
||||||
|
> **promise → 未来值的手动操控**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
我可以帮你 **把之前的“拷贝大文件”项目升级成 async + future 版本**,实现 **多线程拷贝 + 返回每个块的字节数 + 进度统计**,让你真正把线程+future全套用起来。
|
||||||
|
|
||||||
|
你想试吗?
|
||||||
319
Thread/Atomic.md
Normal file
319
Thread/Atomic.md
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
哈哈,**行,这套并发三件套我们直接收官** 😄
|
||||||
|
你已经把 `<thread>`、`<mutex>` 啃完了,现在这个 `<atomic>` **是精华也是雷区**。
|
||||||
|
我给你一份**能当“原子操作说明书”的完整速查版**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 一、`<atomic>` 是干嘛的?一句话版
|
||||||
|
|
||||||
|
> **`<atomic>` 用来做“无锁但安全”的共享小数据。**
|
||||||
|
|
||||||
|
关键词只有三个:
|
||||||
|
|
||||||
|
* 小
|
||||||
|
* 简单
|
||||||
|
* 高频
|
||||||
|
|
||||||
|
典型:计数器、标志位、状态机。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 二、`std::atomic<T>` 基础
|
||||||
|
|
||||||
|
## 1️⃣ 定义原子变量
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
std::atomic<int> x{0};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 支持的常见类型
|
||||||
|
|
||||||
|
* `int / bool / long`
|
||||||
|
* 指针
|
||||||
|
* 枚举
|
||||||
|
* trivially copyable 类型(有限)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2️⃣ 读 / 写(load / store)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
x.store(10);
|
||||||
|
int v = x.load();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 默认内存序
|
||||||
|
|
||||||
|
* `memory_order_seq_cst`(最安全、最慢)
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
* `store`:❌ 无
|
||||||
|
* `load`:✔ 返回值
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3️⃣ 原子赋值(像普通变量一样)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
x = 5;
|
||||||
|
int y = x;
|
||||||
|
```
|
||||||
|
|
||||||
|
✔ 语法糖
|
||||||
|
✔ 等价于 `store / load`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 三、原子读改写(重点)
|
||||||
|
|
||||||
|
## 4️⃣ `fetch_add` / `fetch_sub`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int old = x.fetch_add(1);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 语义
|
||||||
|
|
||||||
|
* 返回 **修改前的值**
|
||||||
|
* 然后再加
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
x++; // 等价 fetch_add(1)
|
||||||
|
--x; // fetch_sub(1)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
✔ 修改前的值
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5️⃣ `exchange()` —— 原子替换
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int old = x.exchange(42);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 作用
|
||||||
|
|
||||||
|
* 原子地:
|
||||||
|
|
||||||
|
* old ← x
|
||||||
|
* x ← 42
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
✔ 旧值
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6️⃣ `compare_exchange_weak / strong`(最难)
|
||||||
|
|
||||||
|
### 用途
|
||||||
|
|
||||||
|
> **CAS(Compare And Swap)**
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int expected = 10;
|
||||||
|
x.compare_exchange_strong(expected, 20);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 行为
|
||||||
|
|
||||||
|
* 如果 `x == expected`
|
||||||
|
|
||||||
|
* `x = 20`
|
||||||
|
* 返回 `true`
|
||||||
|
* 否则
|
||||||
|
|
||||||
|
* `expected = x`
|
||||||
|
* 返回 `false`
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
✔ `bool`
|
||||||
|
|
||||||
|
### weak vs strong
|
||||||
|
|
||||||
|
| 版本 | 特点 |
|
||||||
|
| -------- | ---------- |
|
||||||
|
| `weak` | 可能失败(用于循环) |
|
||||||
|
| `strong` | 不会伪失败 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 四、指针原子(很实用)
|
||||||
|
|
||||||
|
## 7️⃣ `std::atomic<T*>`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::atomic<Node*> p;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 常用操作
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
Node* old = p.load();
|
||||||
|
p.store(new_node);
|
||||||
|
```
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
p.compare_exchange_strong(old, new_node);
|
||||||
|
```
|
||||||
|
|
||||||
|
👉 无锁链表、对象切换神器
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 五、内存序(进阶但你必须知道)
|
||||||
|
|
||||||
|
> **默认你可以全用 `seq_cst`,不会错。**
|
||||||
|
|
||||||
|
### 常见枚举
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::memory_order_relaxed
|
||||||
|
std::memory_order_acquire
|
||||||
|
std::memory_order_release
|
||||||
|
std::memory_order_acq_rel
|
||||||
|
std::memory_order_seq_cst
|
||||||
|
```
|
||||||
|
|
||||||
|
### 什么时候用?
|
||||||
|
|
||||||
|
| 场景 | 内存序 |
|
||||||
|
| --------- | ----------------- |
|
||||||
|
| 简单计数 | relaxed |
|
||||||
|
| 生产者 / 消费者 | acquire / release |
|
||||||
|
| 不想翻车 | seq_cst |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例(生产者-消费者)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
data = 42;
|
||||||
|
flag.store(true, memory_order_release);
|
||||||
|
```
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
while (!flag.load(memory_order_acquire));
|
||||||
|
use(data);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 六、原子标志(最轻量)
|
||||||
|
|
||||||
|
## 8️⃣ `std::atomic_flag`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::atomic_flag f = ATOMIC_FLAG_INIT;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 操作
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
f.test_and_set(); // 返回旧值
|
||||||
|
f.clear();
|
||||||
|
```
|
||||||
|
|
||||||
|
✔ 自旋锁
|
||||||
|
✔ 最底层
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 七、等待 / 通知(C++20)
|
||||||
|
|
||||||
|
## 9️⃣ `wait / notify_one / notify_all`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
x.wait(0);
|
||||||
|
x.store(1);
|
||||||
|
x.notify_one();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 作用
|
||||||
|
|
||||||
|
* 原子级别的条件变量
|
||||||
|
* 无 mutex
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
❌ 无
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 八、完整 API 总表(收藏)
|
||||||
|
|
||||||
|
| API | 作用 | 返回值 |
|
||||||
|
| --------------------------- | ------- | ---- |
|
||||||
|
| `atomic<T>` | 原子变量 | — |
|
||||||
|
| `load()` | 原子读 | T |
|
||||||
|
| `store()` | 原子写 | void |
|
||||||
|
| `exchange()` | 替换 | T |
|
||||||
|
| `fetch_add()` | 加 | T |
|
||||||
|
| `fetch_sub()` | 减 | T |
|
||||||
|
| `compare_exchange_weak()` | CAS | bool |
|
||||||
|
| `compare_exchange_strong()` | CAS | bool |
|
||||||
|
| `atomic_flag` | 最小原子 | — |
|
||||||
|
| `test_and_set()` | 设置并返回旧值 | bool |
|
||||||
|
| `clear()` | 清空 | void |
|
||||||
|
| `wait()` | 等待 | void |
|
||||||
|
| `notify_one()` | 唤醒一个 | void |
|
||||||
|
| `notify_all()` | 唤醒全部 | void |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 九、什么时候用 atomic?一句判断法
|
||||||
|
|
||||||
|
> **如果共享数据:**
|
||||||
|
>
|
||||||
|
> * 类型简单
|
||||||
|
> * 操作简单
|
||||||
|
> * 不涉及多个变量一致性
|
||||||
|
|
||||||
|
👉 **atomic**
|
||||||
|
|
||||||
|
否则:
|
||||||
|
|
||||||
|
👉 **mutex**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🔥 十、新手 atomic 四大雷区(务必避开)
|
||||||
|
|
||||||
|
1️⃣ **以为 atomic 能解决一切**(不能)
|
||||||
|
2️⃣ **多个 atomic 组合 ≠ 原子整体**
|
||||||
|
3️⃣ **乱用 relaxed**
|
||||||
|
4️⃣ **atomic + 普通变量混用无同步**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 十一、你现在的并发地图已经完整了
|
||||||
|
|
||||||
|
```text
|
||||||
|
thread → 干活
|
||||||
|
mutex → 防抢
|
||||||
|
atomic → 快而小
|
||||||
|
future → 拿结果
|
||||||
|
```
|
||||||
|
|
||||||
|
你现在完全可以:
|
||||||
|
|
||||||
|
* 看懂并发代码
|
||||||
|
* 写不翻车的并发
|
||||||
|
* 判断“该不该多线程”
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
如果你愿意,**最后一关**我可以带你做:
|
||||||
|
|
||||||
|
* 🔥 一个 **无锁队列(atomic + CAS)**
|
||||||
|
* 🔥 或把你之前那个拷贝工具 **升级成 atomic 进度条**
|
||||||
|
* 🔥 或讲 **C++ 内存模型白话终极版**
|
||||||
|
|
||||||
|
你点哪个,我收官。
|
||||||
304
Thread/Mutex.md
Normal file
304
Thread/Mutex.md
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
哈哈,**好,这一波咱把 `<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`(线程协作)**
|
||||||
|
|
||||||
|
你选一个,我继续给你“端锅”。
|
||||||
279
Thread/Thread.md
Normal file
279
Thread/Thread.md
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
哈哈哈哈,**太正常了**,这玩意儿本来就反直觉 😄
|
||||||
|
行,我给你一份**能当速查表用的 `<thread>` 全家桶说明**,不讲虚的,只讲:
|
||||||
|
|
||||||
|
* 干嘛用
|
||||||
|
* 怎么调
|
||||||
|
* 有没有返回值
|
||||||
|
* 什么时候该用 / 不该用
|
||||||
|
|
||||||
|
你看完这一份,再回头看你之前的代码,会突然“哦原来如此”。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 一、`<thread>` 到底提供了什么?
|
||||||
|
|
||||||
|
严格来说 `<thread>` **只解决三件事**:
|
||||||
|
|
||||||
|
1. 创建线程
|
||||||
|
2. 控制线程生命周期
|
||||||
|
3. 获取线程身份 / 状态
|
||||||
|
|
||||||
|
👉 **不负责:**
|
||||||
|
|
||||||
|
* 返回值
|
||||||
|
* 同步
|
||||||
|
* 通信
|
||||||
|
|
||||||
|
那些是 `<future>`、`<mutex>`、`<atomic>` 的活。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 二、`std::thread`(核心中的核心)
|
||||||
|
|
||||||
|
## 1️⃣ 构造线程(创建并立刻运行)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
void foo(int x) {}
|
||||||
|
|
||||||
|
std::thread t(foo, 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 本质
|
||||||
|
|
||||||
|
* 创建 OS 线程
|
||||||
|
* 立刻执行 `foo(10)`
|
||||||
|
|
||||||
|
### 参数规则
|
||||||
|
|
||||||
|
* **按值拷贝**
|
||||||
|
* 要引用必须用 `std::ref`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int x = 10;
|
||||||
|
thread t(foo, ref(x));
|
||||||
|
```
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
❌ **没有**
|
||||||
|
(线程函数的返回值会被丢弃)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2️⃣ `join()` —— 等线程干完
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
t.join();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 作用
|
||||||
|
|
||||||
|
* 阻塞当前线程
|
||||||
|
* 直到 `t` 执行完
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
❌ 无
|
||||||
|
|
||||||
|
### 必须记住的铁律
|
||||||
|
|
||||||
|
> **一个 `std::thread`:
|
||||||
|
> 要么 `join()`,要么 `detach()`,否则程序直接 `std::terminate()`**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3️⃣ `detach()` —— 放生线程(慎用)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
t.detach();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 作用
|
||||||
|
|
||||||
|
* 线程变成后台线程
|
||||||
|
* 主线程不再管它
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
❌ 无
|
||||||
|
|
||||||
|
### 什么时候能用?
|
||||||
|
|
||||||
|
* 日志
|
||||||
|
* 心跳
|
||||||
|
* 不依赖任何外部对象
|
||||||
|
|
||||||
|
### 新手忠告
|
||||||
|
|
||||||
|
> **90% 的 `detach()` 都是 bug**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4️⃣ `joinable()` —— 能不能 join?
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if (t.joinable()) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 什么时候 false?
|
||||||
|
|
||||||
|
* 已 join
|
||||||
|
* 已 detach
|
||||||
|
* 默认构造的 thread
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5️⃣ `get_id()` —— 线程 ID
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::thread::id id = t.get_id();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 用途
|
||||||
|
|
||||||
|
* 调试
|
||||||
|
* 日志
|
||||||
|
* 判断是不是同一个线程
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6️⃣ `std::this_thread::get_id()`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
```
|
||||||
|
|
||||||
|
👉 获取**当前线程**的 ID
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 三、线程“辅助函数”(非常常用)
|
||||||
|
|
||||||
|
## 7️⃣ `sleep_for()` —— 休眠一段时间
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <chrono>
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(100ms);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 作用
|
||||||
|
|
||||||
|
* 让当前线程休眠
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
❌ 无
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8️⃣ `sleep_until()` —— 睡到某个时间点
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto t = chrono::steady_clock::now() + 1s;
|
||||||
|
std::this_thread::sleep_until(t);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9️⃣ `yield()` —— 让出 CPU
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::this_thread::yield();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 作用
|
||||||
|
|
||||||
|
* 告诉调度器:
|
||||||
|
“我不急,你先跑别人”
|
||||||
|
|
||||||
|
### 用途
|
||||||
|
|
||||||
|
* 自旋锁
|
||||||
|
* 低优先级任务
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 四、`hardware_concurrency()` —— CPU 有多少核?
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
unsigned n = std::thread::hardware_concurrency();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
|
||||||
|
* 建议线程数
|
||||||
|
* **可能是 0(不保证)**
|
||||||
|
|
||||||
|
👉 常见用法:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int n = max(1u, thread::hardware_concurrency());
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 五、所有 `<thread>` API 一览表(重点)
|
||||||
|
|
||||||
|
| API | 作用 | 返回值 |
|
||||||
|
| ---------------------------- | ------- | ------------ |
|
||||||
|
| `thread(f, args...)` | 创建并运行线程 | — |
|
||||||
|
| `join()` | 等线程结束 | void |
|
||||||
|
| `detach()` | 放生线程 | void |
|
||||||
|
| `joinable()` | 能否 join | bool |
|
||||||
|
| `get_id()` | 线程 ID | `thread::id` |
|
||||||
|
| `this_thread::get_id()` | 当前线程 ID | `thread::id` |
|
||||||
|
| `this_thread::sleep_for()` | 休眠 | void |
|
||||||
|
| `this_thread::sleep_until()` | 睡到时间点 | void |
|
||||||
|
| `this_thread::yield()` | 让出 CPU | void |
|
||||||
|
| `hardware_concurrency()` | CPU 线程数 | unsigned |
|
||||||
|
|
||||||
|
📌 **没有一个 API 有“业务返回值”**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 六、那“返回值”到底去哪了?
|
||||||
|
|
||||||
|
👉 **根本不在 `<thread>`**
|
||||||
|
|
||||||
|
你要记住这个分工:
|
||||||
|
|
||||||
|
| 需求 | 用什么 |
|
||||||
|
| ------ | ---------------- |
|
||||||
|
| 跑线程 | `std::thread` |
|
||||||
|
| 拿返回值 | `std::future` |
|
||||||
|
| 简单并发任务 | `std::async` |
|
||||||
|
| 同步 | `mutex / atomic` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 七、你现在可以这样理解(非常重要)
|
||||||
|
|
||||||
|
> **`std::thread` ≈ C 语言 `pthread_create`
|
||||||
|
> `std::async` ≈ “有返回值的线程函数”**
|
||||||
|
|
||||||
|
所以你刚才的崩溃是合理的 😂
|
||||||
|
你是拿 **pthread 的心态**,期待 **Java 的体验**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 八、给你一条“永远不翻车”的使用准则
|
||||||
|
|
||||||
|
> **如果你想要返回值:
|
||||||
|
> ❌ 不要用 `std::thread`
|
||||||
|
> ✅ 用 `std::async`**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
如果你愿意,下一步我可以帮你做三件事之一:
|
||||||
|
|
||||||
|
1️⃣ 把你刚才那个项目 **改成 async 版本**
|
||||||
|
2️⃣ 给你一份 **`<future>` 函数大全(和这份一样清楚)**
|
||||||
|
3️⃣ 手把手写一个 **线程池(真正工程级)**
|
||||||
|
|
||||||
|
你挑。
|
||||||
Reference in New Issue
Block a user