From c272957d4a13fe859185c0efa47d2f9edb340f90 Mon Sep 17 00:00:00 2001 From: openclaw Date: Mon, 23 Mar 2026 23:44:16 +0000 Subject: [PATCH] Add Chapter 5 Illustrated Version with Mermaid Diagrams for GMP, Channel, and Sync Primitives --- chapters/chapter-5-concurrency-illustrated.md | 328 ++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 chapters/chapter-5-concurrency-illustrated.md diff --git a/chapters/chapter-5-concurrency-illustrated.md b/chapters/chapter-5-concurrency-illustrated.md new file mode 100644 index 0000000..1831185 --- /dev/null +++ b/chapters/chapter-5-concurrency-illustrated.md @@ -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 Channel:Goroutine 间的通信(图解) + +### 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 设计、部署