356 lines
5.2 KiB
Markdown
356 lines
5.2 KiB
Markdown
好,这个问题问得**非常到位**。
|
||
我直接给你一份 **「线程 / 并发编程全景图」**,从**你现在的水平**一路铺到**系统级 / 工程级**,不糊、不玄、不跳步。
|
||
|
||
你可以把这条线当成:
|
||
|
||
> **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 地狱图”**
|
||
|
||
你选一个,我陪你打到懂为止 😈
|