C with classes
This commit is contained in:
355
Thread/readme.md
Normal file
355
Thread/readme.md
Normal file
@@ -0,0 +1,355 @@
|
||||
好,这个问题问得**非常到位**。
|
||||
我直接给你一份 **「线程 / 并发编程全景图」**,从**你现在的水平**一路铺到**系统级 / 工程级**,不糊、不玄、不跳步。
|
||||
|
||||
你可以把这条线当成:
|
||||
|
||||
> **C / C++ 程序员从“会用线程”到“能写并发系统”的路线图**
|
||||
|
||||
---
|
||||
|
||||
# 一、线程到底是什么(地基)
|
||||
|
||||
### 1️⃣ 线程 vs 进程(必须先立住)
|
||||
|
||||
| 对象 | 是否独立地址空间 | 开销 | 通信 |
|
||||
| -- | ---------- | -- | ------ |
|
||||
| 进程 | ✅ 有 | 大 | IPC |
|
||||
| 线程 | ❌ 共享进程地址空间 | 小 | 直接共享内存 |
|
||||
|
||||
👉 **线程共享:**
|
||||
|
||||
* 全局变量
|
||||
* 堆内存
|
||||
* 文件描述符
|
||||
|
||||
👉 **线程私有:**
|
||||
|
||||
* 栈
|
||||
* 寄存器
|
||||
* 程序计数器
|
||||
|
||||
⚠️ **线程 = 并发的根源 + BUG 的源头**
|
||||
|
||||
---
|
||||
|
||||
# 二、线程基础 API(你已经碰到的部分)
|
||||
|
||||
## 2️⃣ 线程生命周期
|
||||
|
||||
以 C++ 为例:
|
||||
|
||||
```cpp
|
||||
std::thread t(func);
|
||||
t.join();
|
||||
```
|
||||
|
||||
完整状态流转:
|
||||
|
||||
```
|
||||
创建 → 就绪 → 运行 → 结束
|
||||
↑
|
||||
被抢占
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ join / detach(线程管理)
|
||||
|
||||
| 操作 | 作用 |
|
||||
| ---------- | ---------- |
|
||||
| `join()` | 等线程结束 |
|
||||
| `detach()` | 后台运行,无法再控制 |
|
||||
|
||||
⚠️ **一个线程只能 join 一次**
|
||||
|
||||
---
|
||||
|
||||
# 三、线程最核心的问题:数据竞争 💥
|
||||
|
||||
## 4️⃣ 什么是数据竞争(Race Condition)
|
||||
|
||||
```cpp
|
||||
int x = 0;
|
||||
|
||||
void f() {
|
||||
x++; // ❌ 非原子操作
|
||||
}
|
||||
```
|
||||
|
||||
两个线程同时执行:
|
||||
|
||||
```
|
||||
load x
|
||||
add 1
|
||||
store x
|
||||
```
|
||||
|
||||
👉 结果可能是:
|
||||
|
||||
* 1
|
||||
* 2
|
||||
* 乱七八糟(UB)
|
||||
|
||||
📌 **没有同步 = 行为未定义**
|
||||
|
||||
---
|
||||
|
||||
# 四、同步原语(并发的骨架)
|
||||
|
||||
## 5️⃣ 互斥锁(Mutex)——第一大杀器
|
||||
|
||||
```cpp
|
||||
std::mutex m;
|
||||
|
||||
void f() {
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
x++;
|
||||
}
|
||||
```
|
||||
|
||||
### Mutex 解决什么?
|
||||
|
||||
* 保证 **临界区** 同一时间只有一个线程进入
|
||||
|
||||
### 常见变种:
|
||||
|
||||
* `mutex`
|
||||
* `recursive_mutex`
|
||||
* `timed_mutex`
|
||||
* `shared_mutex`(读多写少)
|
||||
|
||||
---
|
||||
|
||||
## 6️⃣ 死锁(锁的黑暗面)☠️
|
||||
|
||||
```cpp
|
||||
lock(A);
|
||||
lock(B);
|
||||
```
|
||||
|
||||
另一线程:
|
||||
|
||||
```cpp
|
||||
lock(B);
|
||||
lock(A);
|
||||
```
|
||||
|
||||
💀 **两人对视,程序永恒**
|
||||
|
||||
### 解决方法:
|
||||
|
||||
* 统一加锁顺序
|
||||
* `std::lock()`
|
||||
* 层级锁(lock hierarchy)
|
||||
|
||||
---
|
||||
|
||||
## 7️⃣ 条件变量(Condition Variable)
|
||||
|
||||
👉 **“我等一个条件成立”**
|
||||
|
||||
```cpp
|
||||
std::condition_variable cv;
|
||||
std::mutex m;
|
||||
bool ready = false;
|
||||
|
||||
void worker() {
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait(lock, []{ return ready; });
|
||||
}
|
||||
```
|
||||
|
||||
用途:
|
||||
|
||||
* 生产者-消费者
|
||||
* 线程间通知
|
||||
|
||||
⚠️ **必须配合 mutex 使用**
|
||||
|
||||
---
|
||||
|
||||
# 五、原子操作(无锁并发)
|
||||
|
||||
## 8️⃣ `std::atomic`(你迟早会用)
|
||||
|
||||
```cpp
|
||||
std::atomic<int> x{0};
|
||||
x++;
|
||||
```
|
||||
|
||||
特点:
|
||||
|
||||
* 无锁(可能是 CPU 指令)
|
||||
* 不会数据竞争
|
||||
|
||||
### 内存序(进阶但重要):
|
||||
|
||||
* `memory_order_relaxed`
|
||||
* `acquire / release`
|
||||
* `seq_cst`
|
||||
|
||||
👉 **这是并发里最难的一块**
|
||||
|
||||
---
|
||||
|
||||
# 六、线程模型 & 执行结构
|
||||
|
||||
## 9️⃣ 线程池(工程级必备)
|
||||
|
||||
问题:
|
||||
|
||||
* 线程创建很贵
|
||||
|
||||
解决:
|
||||
|
||||
```text
|
||||
任务队列 + 固定数量线程
|
||||
```
|
||||
|
||||
用途:
|
||||
|
||||
* Web 服务器
|
||||
* 游戏引擎
|
||||
* 编译器
|
||||
|
||||
---
|
||||
|
||||
## 🔟 任务 vs 线程(现代并发思想)
|
||||
|
||||
| 概念 | 含义 |
|
||||
| -- | ---- |
|
||||
| 线程 | 执行单元 |
|
||||
| 任务 | 要做的事 |
|
||||
|
||||
C++:
|
||||
|
||||
* `std::async`
|
||||
* `std::future`
|
||||
* `std::promise`
|
||||
|
||||
---
|
||||
|
||||
# 七、系统层线程(你会很喜欢)
|
||||
|
||||
## 1️⃣1️⃣ OS 级线程实现
|
||||
|
||||
### Linux:
|
||||
|
||||
* `pthread_create`
|
||||
* `futex`
|
||||
* 调度器(CFS)
|
||||
|
||||
### Windows:
|
||||
|
||||
* `CreateThread`
|
||||
* `WaitForSingleObject`
|
||||
|
||||
👉 **C++ `std::thread` 本质是封装**
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣2️⃣ 用户态线程(协程)
|
||||
|
||||
| 类型 | 是否 OS 线程 |
|
||||
| ----------- | -------- |
|
||||
| std::thread | 是 |
|
||||
| 协程 | 否 |
|
||||
|
||||
特点:
|
||||
|
||||
* 切换快
|
||||
* 不能抢占
|
||||
* 需要调度器
|
||||
|
||||
例子:
|
||||
|
||||
* C++20 coroutine
|
||||
* Go goroutine
|
||||
* libco / boost::coroutine
|
||||
|
||||
---
|
||||
|
||||
# 八、并发的“终极敌人”
|
||||
|
||||
## 1️⃣3️⃣ 可见性 & 内存模型
|
||||
|
||||
问题:
|
||||
|
||||
```cpp
|
||||
flag = true; // 线程 A
|
||||
while(!flag) {} // 线程 B
|
||||
```
|
||||
|
||||
⚠️ **可能死循环**
|
||||
|
||||
原因:
|
||||
|
||||
* CPU cache
|
||||
* 编译器重排
|
||||
|
||||
解决:
|
||||
|
||||
* atomic
|
||||
* mutex
|
||||
* memory barrier
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣4️⃣ 假共享(False Sharing)
|
||||
|
||||
```cpp
|
||||
struct {
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
```
|
||||
|
||||
a / b 在同一 cache line
|
||||
两个线程写不同变量 → 性能暴死
|
||||
|
||||
---
|
||||
|
||||
# 九、你这个阶段最推荐的学习顺序 ✅
|
||||
|
||||
结合你**超偏底层 + 喜欢 C**的特点,我给你一个**实操导向路线**:
|
||||
|
||||
### 第一阶段(立刻)
|
||||
|
||||
* `std::thread`
|
||||
* `join / detach`
|
||||
* `mutex`
|
||||
* 数据竞争概念
|
||||
|
||||
### 第二阶段
|
||||
|
||||
* 生产者-消费者
|
||||
* 条件变量
|
||||
* 死锁分析
|
||||
|
||||
### 第三阶段(爽点)
|
||||
|
||||
* `std::atomic`
|
||||
* lock-free 计数器
|
||||
* 内存序
|
||||
|
||||
### 第四阶段(硬核)
|
||||
|
||||
* pthread + futex
|
||||
* 调度器原理
|
||||
* 协程实现
|
||||
|
||||
---
|
||||
|
||||
# 十、一句话总结(你现在的位置)
|
||||
|
||||
> **你已经站在“并发编程”的门口了**
|
||||
> 接下来学的不是“语法”,而是:
|
||||
> 👉 **CPU、内存、调度、同步的真实世界**
|
||||
|
||||
如果你愿意,下一步我可以直接带你:
|
||||
|
||||
* **手写一个线程池(C / C++)**
|
||||
* 或 **用汇编 + C 看 atomic 指令**
|
||||
* 或 **画一张“并发 BUG 地狱图”**
|
||||
|
||||
你选一个,我陪你打到懂为止 😈
|
||||
Reference in New Issue
Block a user