Thread Learning
This commit is contained in:
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