Files
test/chapters/chapter-5-concurrency-illustrated.md

329 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第五章:并发编程 —— Goroutine 与 Channel 的艺术(图解版)
> **本章目标**:深入理解 Go 并发的核心机制,**配合大量图解**,掌握 Goroutine 调度原理、Channel 通信模式、同步原语等。
## 5.1 并发基础与 Goroutine
### 5.1.1 并发 vs 并行(图解)
```mermaid
graph LR
subgraph 并发 Concurrency
A[任务 1] -->|交替执行 | B(时间片 1)
A -->|交替执行 | C(时间片 3)
D[任务 2] -->|交替执行 | B
D -->|交替执行 | C
style A fill:#f9f,stroke:#333
style D fill:#f9f,stroke:#333
end
subgraph 并行 Parallelism
E[任务 1] -->|同时执行 | F(CPU 核心 1)
G[任务 2] -->|同时执行 | H(CPU 核心 2)
style E fill:#9f9,stroke:#333
style G fill:#9f9,stroke:#333
end
```
- **并发**:宏观上同时,微观上**交替**(单核也能实现)。
- **并行**:微观上**同时**(需要多核 CPU
---
## 5.2 GMP 调度模型 ⭐ 核心重点(图解)
Go 的并发性能得益于其独特的 **GMP 调度模型**
### 5.2.1 GMP 模型架构(图解)
```mermaid
graph TB
subgraph "Go Runtime 用户态调度"
P1[P1: 逻辑处理器]
P2[P2: 逻辑处理器]
P3[P3: 逻辑处理器]
Q1[本地 G 队列]
Q2[本地 G 队列]
Q3[本地 G 队列]
P1 --- Q1
P2 --- Q2
P3 --- Q3
GlobalQ[全局 G 队列]
GlobalQ -.-> P1
GlobalQ -.-> P2
GlobalQ -.-> P3
end
subgraph "操作系统内核态"
M1[M1: 线程]
M2[M2: 线程]
M3[M3: 线程]
M1 <--> P1
M2 <--> P2
M3 <--> P3
end
subgraph "Goroutine 实体"
G1[G1: 协程]
G2[G2: 协程]
G3[G3: 协程]
G4[G4: 协程]
G1 --> Q1
G2 --> Q1
G3 --> Q2
G4 --> Q3
end
style P1 fill:#ffeb3b,stroke:#333
style M1 fill:#2196f3,stroke:#333,color:#fff
style G1 fill:#4caf50,stroke:#333,color:#fff
```
**图解说明**
1. **G (Goroutine)**:绿色的协程,包含栈、指令指针。
2. **P (Processor)**:黄色的逻辑处理器,管理 G 队列,**数量 = CPU 核数**。
3. **M (Machine)**:蓝色的操作系统线程,真正执行代码。
4. **调度**M 绑定 P从 P 的本地队列取 G 执行。
### 5.2.2 调度流程:从创建到执行
```mermaid
sequenceDiagram
participant App as 应用程序
participant G as Goroutine
participant P as P (逻辑处理器)
participant M as M (线程)
participant OS as 操作系统
App->>P: 创建 G放入本地队列
P->>M: M 绑定 P获取 G
M->>G: 执行 G 代码
alt G 阻塞 (如 IO)
G->>OS: 发起系统调用
OS-->>M: M 阻塞
P->>P: 寻找新 M 继续调度其他 G
Note right of P: P 不阻塞,继续工作!
else G 完成
G->>M: 执行完毕
M->>P: 归还 P
end
```
### 5.2.3 工作窃取 (Work Stealing)
当某个 P 的队列为空时,它会从其他 P 的队列**窃取**一半的 G。
```
P1 队列:[G1, G2, G3, G4, G5] (满载)
P2 队列:[] (空闲)
P2 发现空 -> 向 P1 请求 -> P1 给 G4, G5
P1 队列:[G1, G2, G3]
P2 队列:[G4, G5] <-- 窃取成功!
```
---
## 5.3 ChannelGoroutine 间的通信(图解)
### 5.3.1 无缓冲 Channel (同步)
发送和接收必须**同时就绪**,否则阻塞。
```
发送方 (go func) 接收方 (main)
| |
| ch <- 42 |
| (阻塞等待) |
| <-------------------> | 数据传递 (42)
| | (同时发生)
v v
发送完成 接收完成
```
### 5.3.2 有缓冲 Channel (异步)
缓冲区未满时,发送不阻塞;缓冲区非空时,接收不阻塞。
```
缓冲区容量 = 2
发送方 缓冲区 接收方
| [ ] [ ] |
| ch <- 1 (成功) [1] [ ] |
| ch <- 2 (成功) [1] [2] |
| ch <- 3 (阻塞!) [1] [2] | (满了)
| [1] [2] | <-val (接收 1)
| [ ] [2] |
| ch <- 3 (成功!) [3] [2] |
| [3] [2] | <-val (接收 2)
| [3] [ ] |
```
### 5.3.3 Channel 状态机
```mermaid
stateDiagram-v2
[*] --> 未初始化
未初始化 --> 打开make
打开 --> 打开:发送/接收
打开 --> 关闭close()
关闭 --> 关闭:接收 (返回零值)
关闭 --> [*]GC 回收
打开 --> [*]GC 回收
note right of 打开
发送阻塞:缓冲区满
接收阻塞:缓冲区空
end note
```
---
## 5.4 同步原语 (图解)
### 5.4.1 WaitGroup 计数原理
```
初始Counter = 0
Goroutine 1: Add(1) -> Counter = 1
Goroutine 2: Add(1) -> Counter = 2
Goroutine 3: Add(1) -> Counter = 3
Goroutine 1: Done() -> Counter = 2
Goroutine 2: Done() -> Counter = 1
Goroutine 3: Done() -> Counter = 0 (唤醒 Wait())
```
### 5.4.2 Mutex 锁状态
```mermaid
stateDiagram-v2
[*] --> 空闲:初始
空闲 --> 占用Lock()
占用 --> 空闲Unlock()
占用 --> 等待队列Lock() (阻塞)
等待队列 --> 空闲Unlock() (唤醒)
```
### 5.4.3 RWMutex 读写锁
```
读锁 (RLock):允许多个读者同时持有
写锁 (Lock):排他,只能有一个写者,且不能有读者
状态图:
[空闲] --R--> [多读] --R--> [多读]
| |
L L
v v
[写] <--------- [多读] (写者等待)
|
U
v
[空闲]
```
---
## 5.5 原子操作 (图解)
```
内存地址0x1000 (值 = 5)
Goroutine 1: atomic.Add(0x1000, 1) -> 硬件级 CAS -> 值 = 6
Goroutine 2: atomic.Add(0x1000, 1) -> 硬件级 CAS -> 值 = 7
(无需锁CPU 指令直接保证原子性)
```
---
## 5.6 Context 传递 (图解)
```mermaid
graph LR
Parent[父 Context] -->|WithTimeout| Child1[子 Context 1]
Parent -->|WithValue| Child2[子 Context 2]
Parent -- 取消 --> Cancel1[取消信号]
Child1 -- 传播 --> Cancel1
Child2 -- 传播 --> Cancel1
style Parent fill:#ff9800,stroke:#333
style Cancel1 fill:#f44336,stroke:#333,color:#fff
```
- **取消传播**:父 Context 取消,所有子 Context 自动取消。
- **超时传播**:父 Context 超时,子 Context 也超时。
---
## 5.7 竞态检测 (图解)
```
竞态 (Race Condition)
Goroutine 1: 读 变量 X
Goroutine 2: 写 变量 X
(无锁,同时发生) -> 数据不一致!
Race Detector 检测:
Goroutine 1: 读 X (记录时间 T1)
Goroutine 2: 写 X (记录时间 T2)
T1 和 T2 重叠 -> 报告竞态!
```
---
## 5.8 并发设计模式 (图解)
### 5.8.1 管道 (Pipeline)
```
Stage 1 (生成) Stage 2 (平方) Stage 3 (输出)
[1,2,3] -----> [1,4,9] -----> 打印
| | |
(Chan A) (Chan B) (结果)
```
### 5.8.2 工作池 (Worker Pool)
```
任务队列 (100 个)
|
v
+-------------------+
| Worker 1 (处理) |
| Worker 2 (处理) | (并发执行)
| Worker 3 (处理) |
+-------------------+
|
v
结果队列
```
---
*(本章其余部分保持原有文字内容,此处仅展示图解核心)*
---
## 🎨 图解总结
1. **GMP 模型**P 是调度器M 是执行者G 是任务。
2. **Channel**:无缓冲是同步,有缓冲是异步。
3. **锁**Mutex 互斥RWMutex 读写分离。
4. **Context**:树状结构,取消信号自顶向下传播。
5. **管道**:数据流式处理,解耦各阶段。
---
**代码仓库位置**https://giter.top/openclaw/test/tree/main/chapters/chapter-5
**下一章预告**HTTP 服务器、路由、中间件、数据库连接池、RESTful API 设计、部署