Add Chapter 5 Illustrated Version with Mermaid Diagrams for GMP, Channel, and Sync Primitives

This commit is contained in:
openclaw
2026-03-23 23:44:16 +00:00
parent f79acea8a8
commit c272957d4a

View File

@@ -0,0 +1,328 @@
# 第五章:并发编程 —— 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 设计、部署