前言
💡 痛点: Go 的 GMP 调度器怎么工作?Channel 有哪些高级用法?Context 怎么正确传递和取消?并发原语(Mutex/RWMutex/Cond/Once/WaitGroup/errgroup)怎么选?生产级并发模式有哪些?
🎯 解决方案: 本文系统覆盖 Go 高并发全链路:GMP 调度器原理与调优、Channel 10 种模式、Context 链式传递与超时、sync 并发原语详解、并发安全 Map、对象池(sync.Pool)、生产级 Worker Pool 与 Fan-out/Fan-in 模式。
#mermaid-svg-sfJHwxSoRLgoNzko{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sfJHwxSoRLgoNzko .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sfJHwxSoRLgoNzko .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sfJHwxSoRLgoNzko .error-icon{fill:#552222;}#mermaid-svg-sfJHwxSoRLgoNzko .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sfJHwxSoRLgoNzko .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sfJHwxSoRLgoNzko .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sfJHwxSoRLgoNzko .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sfJHwxSoRLgoNzko .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sfJHwxSoRLgoNzko .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sfJHwxSoRLgoNzko .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sfJHwxSoRLgoNzko .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sfJHwxSoRLgoNzko .marker.cross{stroke:#333333;}#mermaid-svg-sfJHwxSoRLgoNzko svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sfJHwxSoRLgoNzko p{margin:0;}#mermaid-svg-sfJHwxSoRLgoNzko .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sfJHwxSoRLgoNzko .cluster-label text{fill:#333;}#mermaid-svg-sfJHwxSoRLgoNzko .cluster-label span{color:#333;}#mermaid-svg-sfJHwxSoRLgoNzko .cluster-label span p{background-color:transparent;}#mermaid-svg-sfJHwxSoRLgoNzko .label text,#mermaid-svg-sfJHwxSoRLgoNzko span{fill:#333;color:#333;}#mermaid-svg-sfJHwxSoRLgoNzko .node rect,#mermaid-svg-sfJHwxSoRLgoNzko .node circle,#mermaid-svg-sfJHwxSoRLgoNzko .node ellipse,#mermaid-svg-sfJHwxSoRLgoNzko .node polygon,#mermaid-svg-sfJHwxSoRLgoNzko .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sfJHwxSoRLgoNzko .rough-node .label text,#mermaid-svg-sfJHwxSoRLgoNzko .node .label text,#mermaid-svg-sfJHwxSoRLgoNzko .image-shape .label,#mermaid-svg-sfJHwxSoRLgoNzko .icon-shape .label{text-anchor:middle;}#mermaid-svg-sfJHwxSoRLgoNzko .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sfJHwxSoRLgoNzko .rough-node .label,#mermaid-svg-sfJHwxSoRLgoNzko .node .label,#mermaid-svg-sfJHwxSoRLgoNzko .image-shape .label,#mermaid-svg-sfJHwxSoRLgoNzko .icon-shape .label{text-align:center;}#mermaid-svg-sfJHwxSoRLgoNzko .node.clickable{cursor:pointer;}#mermaid-svg-sfJHwxSoRLgoNzko .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sfJHwxSoRLgoNzko .arrowheadPath{fill:#333333;}#mermaid-svg-sfJHwxSoRLgoNzko .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sfJHwxSoRLgoNzko .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sfJHwxSoRLgoNzko .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sfJHwxSoRLgoNzko .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sfJHwxSoRLgoNzko .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sfJHwxSoRLgoNzko .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sfJHwxSoRLgoNzko .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sfJHwxSoRLgoNzko .cluster text{fill:#333;}#mermaid-svg-sfJHwxSoRLgoNzko .cluster span{color:#333;}#mermaid-svg-sfJHwxSoRLgoNzko div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sfJHwxSoRLgoNzko .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sfJHwxSoRLgoNzko rect.text{fill:none;stroke-width:0;}#mermaid-svg-sfJHwxSoRLgoNzko .icon-shape,#mermaid-svg-sfJHwxSoRLgoNzko .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sfJHwxSoRLgoNzko .icon-shape p,#mermaid-svg-sfJHwxSoRLgoNzko .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sfJHwxSoRLgoNzko .icon-shape .label rect,#mermaid-svg-sfJHwxSoRLgoNzko .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sfJHwxSoRLgoNzko .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sfJHwxSoRLgoNzko .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sfJHwxSoRLgoNzko :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 并发原语
Mutex/RWMutex
sync.Map
并发安全 Map
WaitGroup/errgroup
Once/OnceValue
sync.Pool
对象池
并发模式
Worker Pool
工作池
Fan-out
分发
Pipeline
流水线
Fan-in
合并
GMP 调度器
Goroutine
轻量级线程
Machine
OS 线程
Processor
逻辑处理器
一、GMP 调度器原理
1.1 G/M/P 模型
# ======== GMP 调度器核心概念 ========
#
# G (Goroutine): 轻量级线程
# - 初始栈大小 2KB(可动态增长到 1GB)
# - 创建成本极低(~300ns vs 线程 ~1ms)
# - 由 Go runtime 管理,不依赖 OS
#
# M (Machine): OS 线程
# - 由 OS 管理
# - 最大数量默认 10000(GOMAXPROCS 控制 P 的数量)
# - M 必须绑定一个 P 才能执行 G
#
# P (Processor): 逻辑处理器
# - 数量 = GOMAXPROCS(默认 = CPU 核心数)
# - 每个 P 有一个本地 G 队列(最多 256 个 G)
# - P 是调度的核心,G 在 P 上运行
#
# ======== 调度流程 ========
#
# 1. 创建 G → 放入当前 P 的本地队列
# 2. 本地队列满(256)→ 放入全局队列
# 3. P 执行 G → G 阻塞(syscall/channel)→ M 释放 P
# 4. P 寻找新的 G → 本地队列 → 全局队列 → 其他 P 借取(work stealing)
# 5. G 恢复 → M 重新绑定 P → 继续执行
#
# ======== Work Stealing(工作窃取)=======
#
# 当 P 的本地队列空时:
# 1. 先从全局队列取一批 G(移动 1/2)
# 2. 如果全局队列也空 → 从其他 P 的本地队列窃取一半
# 3. 确保所有 P 都有工作做(避免 CPU 空转)
#
# ======== 系统调用处理 ========
#
# G 发生阻塞系统调用:
# 1. M 与 P 解绑(hand off)
# 2. P 绑定新的 M(或创建新 M)
# 3. 系统调用返回 → G 放入全局队列
# 4. M 等待空闲 P 或进入休眠
#
# ======== 关键参数 ========
#
# GOMAXPROCS: P 的数量(默认 = runtime.NumCPU())
# - 设置太小:CPU 利用率低
# - 设置太大:M 频繁切换,增加开销
# - 推荐:CPU 密集型 = CPU 核心数;IO 密集型 = CPU * 2
#
# GOMEMLIMIT: 内存限制(Go 1.19+)
# - GC 目标内存使用上限
# - 推荐:容器 memory limit 的 90%
# - runtime/debug.SetMemoryLimit(1 << 30) // 1GB
1.2 调度器调优
go
// ======== 调度器调优实战 ========
package main
import (
"fmt"
"runtime"
"runtime/debug"
"sync"
"time"
)
func init() {
// 1. 设置 GOMAXPROCS(CPU 密集型任务)
runtime.GOMAXPROCS(runtime.NumCPU()) // 默认值
// 2. 设置内存限制(Go 1.19+)
debug.SetMemoryLimit(1 << 30) // 1GB(容器环境下推荐)
// 3. 设置 GC 目标百分比
debug.SetGCPercent(100) // 默认 100(堆增长 100% 时触发 GC)
// 4. 禁用 GC(极端场景,不建议)
// debug.SetGCPercent(-1)
}
// ======== Goroutine 泄露检测 ========
func detectGoroutineLeak() {
initialCount := runtime.NumGoroutine()
// 执行业务逻辑
doWork()
finalCount := runtime.NumGoroutine()
if finalCount > initialCount + 10 {
fmt.Printf("Potential goroutine leak: %d -> %d\n", initialCount, finalCount)
}
}
// ======== Goroutine 栈大小监控 ========
func monitorStackSizes() {
for {
time.Sleep(5 * time.Second)
fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine())
fmt.Printf("CPU cores: %d\n", runtime.NumCPU())
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
}
}
// ======== CPU 密集型任务(限制并发)=======
func cpuIntensiveTask() {
// CPU 密集型任务:限制并发 = CPU 核心数
sem := make(chan struct{}, runtime.GOMAXPROCS(0))
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
sem <- struct{}{} // 获取信号量
go func(id int) {
defer wg.Done()
defer func() { <-sem }() // 释放信号量
// CPU 密集计算
result := fibonacci(30)
fmt.Printf("Task %d: %d\n", id, result)
}(i)
}
wg.Wait()
}
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
// ======== IO 密集型任务(增加并发)=======
func ioIntensiveTask() {
// IO 密集型任务:并发可以远大于 CPU 核心数
sem := make(chan struct{}, runtime.GOMAXPROCS(0)*10)
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
sem <- struct{}{}
go func(id int) {
defer wg.Done()
defer func() { <-sem }()
// IO 操作(HTTP 请求、数据库查询等)
time.Sleep(100 * time.Millisecond) // 模拟 IO
}(i)
}
wg.Wait()
}
二、Channel 10 种模式
2.1 基础模式
go
// ======== Channel 模式 1:信号通知 ========
// 单向信号(done channel)
func signalPattern() {
done := make(chan struct{})
go func() {
time.Sleep(2 * time.Second)
close(done) // close 发出信号(所有接收者都能收到)
}()
select {
case <-done:
fmt.Println("Work completed")
case <-time.After(5 * time.Second):
fmt.Println("Timeout")
}
}
// ======== Channel 模式 2:单向 Channel ========
// 类型约束:只读/只写
func directionalChannel() {
// 生产者只能发送
producer := func(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
// 消费者只能接收
consumer := func(ch <-chan int) {
for v := range ch {
fmt.Println(v)
}
}
ch := make(chan int)
go producer(ch)
consumer(ch)
}
// ======== Channel 模式 3:缓冲 Channel ========
// 缓冲大小选择:
// 0:同步(发送方阻塞直到接收方就绪)
// 1:异步单次(最常用)
// N:批量缓冲(N = 生产者数量或预估缓冲量)
func bufferedChannel() {
// 无缓冲(同步)
unbuffered := make(chan int)
// 缓冲 1(最常用)
singleBuffer := make(chan int, 1)
// 缓冲 N(批量)
batchBuffer := make(chan int, 100)
// 使用缓冲 1 的场景:
// 发送方发送一次后不阻塞(缓冲已满则阻塞)
singleBuffer <- 42 // 不阻塞(缓冲区有空间)
v := <-singleBuffer
fmt.Println(v)
}
// ======== Channel 模式 4:关闭 Channel 检测 ========
func closeDetection() {
ch := make(chan int, 5)
ch <- 1
ch <- 2
close(ch)
// 接收关闭的 Channel
v, ok := <-ch // ok = true(值还在缓冲区)
fmt.Println(v, ok) // 1 true
v, ok = <-ch
fmt.Println(v, ok) // 2 true
v, ok = <-ch
fmt.Println(v, ok) // 0 false(缓冲区空,返回零值)
// range 自动检测关闭
for v := range ch {
fmt.Println(v) // 1, 2
}
}
// ======== Channel 模式 5:select 多路复用 ========
func selectPattern() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "result1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "result2"
}()
for i := 0; i < 2; i++ {
select {
case v := <-ch1:
fmt.Println(v)
case v := <-ch2:
fmt.Println(v)
}
}
}
// ======== Channel 模式 6:超时控制 ========
func timeoutPattern() {
ch := make(chan int)
select {
case v := <-ch:
fmt.Println(v)
case <-time.After(3 * time.Second):
fmt.Println("Timeout after 3s")
}
}
// ======== Channel 模式 7:非阻塞发送/接收 ========
func nonBlockingPattern() {
ch := make(chan int, 1)
// 非阻塞发送
select {
case ch <- 42:
fmt.Println("Sent")
default:
fmt.Println("Channel full, skip")
}
// 非阻塞接收
select {
case v := <-ch:
fmt.Println("Received:", v)
default:
fmt.Println("Channel empty, skip")
}
}
// ======== Channel 模式 8:退出信号 ========
func quitSignal() {
quit := make(chan struct{})
data := make(chan int)
go func() {
for {
select {
case v := <-data:
fmt.Println("Processing:", v)
case <-quit:
fmt.Println("Quitting")
return
}
}
}()
data <- 1
data <- 2
close(quit) // 发出退出信号
}
2.2 高级模式
go
// ======== Channel 模式 9:Fan-out/Fan-in ========
// Fan-out: 一个 Channel 分发到多个 Worker
// Fan-in: 多个 Channel 合并到一个 Channel
func fanOutFanIn() {
// 数据源
input := generate(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// Fan-out:分发到 3 个 Worker
worker1 := worker(input)
worker2 := worker(input)
worker3 := worker(input)
// Fan-in:合并 3 个 Worker 的输出
for result := range merge(worker1, worker2, worker3) {
fmt.Println(result)
}
}
func generate(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func worker(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n // 处理
}
close(out)
}()
return out
}
func merge(chans ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
// 为每个输入 Channel 启动一个 Goroutine
for _, ch := range chans {
wg.Add(1)
go func(c <-chan int) {
defer wg.Done()
for v := range c {
out <- v
}
}(ch)
}
// 等待所有输入 Channel 关闭后,关闭输出 Channel
go func() {
wg.Wait()
close(out)
}()
return out
}
// ======== Channel 模式 10:Pipeline 流水线 ========
func pipeline() {
// Stage 1: 生成数据
stage1 := generatePipeline(1, 2, 3, 4, 5)
// Stage 2: 过滤偶数
stage2 := filter(stage1, func(n int) bool { return n%2 == 0 })
// Stage 3: 计算
stage3 := compute(stage2, func(n int) int { return n * n })
// 输出
for result := range stage3 {
fmt.Println(result) // 4, 16
}
}
func generatePipeline(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func filter(in <-chan int, fn func(int) bool) <-chan int {
out := make(chan int)
go func() {
for n := range in {
if fn(n) {
out <- n
}
}
close(out)
}()
return out
}
func compute(in <-chan int, fn func(int) int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- fn(n)
}
close(out)
}()
return out
}
三、Context 链式传递
3.1 Context 四种创建方式
go
// ======== Context 创建方式 ========
// 1. Background(根 Context,永远不会取消)
ctx := context.Background()
// 2. WithCancel(手动取消)
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 建议总是调用 cancel(即使不显式取消)
// 3. WithTimeout(超时自动取消)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 4. WithDeadline(绝对时间取消)
deadline := time.Now().Add(5 * time.Minute)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// ======== Context 链式组合 ========
// 链式组合:从 Background 创建链
func chainContext() {
// Root
root := context.Background()
// 第一层:超时
ctx1, cancel1 := context.WithTimeout(root, 10*time.Minute)
defer cancel1()
// 第二层:附加值
ctx2 := context.WithValue(ctx1, "requestID", "req-123")
// 第三层:再次超时(更短)
ctx3, cancel3 := context.WithTimeout(ctx2, 30*time.Second)
defer cancel3()
// ctx3 包含:
// - 10 分钟超时(父级)
// - requestID(父级)
// - 30 秒超时(本层)
// 检查取消
select {
case <-ctx3.Done():
fmt.Println("Context cancelled:", ctx3.Err())
// context.DeadlineExceeded(超时)
// context.Canceled(手动取消)
}
}
3.2 Context 传递规则
go
// ======== Context 传递规则 ========
//
// 1. Context 作为函数第一个参数(不要放在结构体中)
// 2. 不要传递 nil Context(用 context.TODO() 代替)
// 3. Context 只用于请求级别的数据(不要传业务数据)
// 4. WithValue 用于请求级别的跨 API 数据(requestID、traceID)
// 5. 函数接收 Context,不要存储它
// 6. cancel() 总是调用(避免 Goroutine 泄露)
// ✅ 正确:Context 作为第一个参数
func ProcessRequest(ctx context.Context, data string) error {
// ...
}
// ❌ 错误:Context 放在结构体中
type BadService struct {
ctx context.Context // 不要这样做
}
// ✅ 正确:WithValue 只传请求级别数据
ctx = context.WithValue(ctx, "requestID", "req-123")
ctx = context.WithValue(ctx, "traceID", "trace-456")
// ❌ 错误:WithValue 传业务数据
// ctx = context.WithValue(ctx, "userID", 12345)
// ctx = context.WithValue(ctx, "config", myConfig)
// 业务数据应该通过参数传递,不放在 Context
// ======== Context 实际应用 ========
// HTTP 中间件注入 Context
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 注入 requestID
requestID := uuid.New().String()
ctx := context.WithValue(r.Context(), "requestID", requestID)
// 注入超时
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
// 传递 Context
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// 数据库查询超时
func queryWithTimeout(ctx context.Context, db *sql.DB, query string) (*sql.Rows, error) {
// 创建子 Context(更短超时)
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
return db.QueryContext(ctx, query)
}
// GRPC 方法传递 Context
func (s *Server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
// 检查 Context 取消
if ctx.Err() != nil {
return nil, ctx.Err()
}
// 从 Context 获取 requestID
requestID, _ := ctx.Value("requestID").(string)
log.Printf("GetUser request: %s, requestID: %s", req.ID, requestID)
// 数据库查询(Context 传递)
user, err := s.repo.GetByID(ctx, req.ID)
if err != nil {
return nil, err
}
return user, nil
}
3.3 Context 取消传播
go
// ======== Context 取消传播 ========
// 父 Context 取消 → 所有子 Context 自动取消
func propagateCancellation() {
// 父 Context
parentCtx, parentCancel := context.WithCancel(context.Background())
// 子 Context
childCtx1, childCancel1 := context.WithCancel(parentCtx)
childCancel1() // 调用但不影响父级
childCtx2, _ := context.WithTimeout(parentCtx, 10*time.Second)
// 父级取消 → childCtx2 也取消
parentCancel()
// 检查
fmt.Println(childCtx2.Err()) // context.Canceled(被父级取消)
}
// ======== Goroutine 泄露预防 ========
func preventLeak() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// ✅ 正确:传递 Context,Goroutine 会自动退出
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
return // Context 取消,Goroutine 退出
default:
// 工作逻辑
time.Sleep(1 * time.Second)
}
}
}(ctx) // 传递 Context(不是用外部变量)
// ❌ 错误:不传 Context,Goroutine 永远不退出
// go func() {
// for { // 永远循环
// time.Sleep(1 * time.Second)
// }
// }()
cancel() // 取消后 Goroutine 退出
}
// ======== errgroup:错误收集 + Context 传播 ========
func errgroupPattern() {
g, ctx := errgroup.WithContext(context.Background())
// 任务1
g.Go(func() error {
return fetchURL(ctx, "https://example.com/data1")
})
// 任务2
g.Go(func() error {
return fetchURL(ctx, "https://example.com/data2")
})
// 任务3
g.Go(func() error {
return fetchURL(ctx, "https://example.com/data3")
})
// 等待所有任务完成
if err := g.Wait(); err != nil {
// 任何一个任务出错,其他任务的 Context 自动取消
fmt.Println("Error:", err)
}
}
func fetchURL(ctx context.Context, url string) error {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
return nil
}
四、并发原语详解
4.1 Mutex 与 RWMutex
go
// ======== Mutex(互斥锁)=======
type Counter struct {
mu sync.Mutex
count int
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock() // defer 确保 Unlock
c.count++
}
func (c *Counter) Get() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
// ======== RWMutex(读写锁)=======
// 读操作不互斥,写操作互斥
type Cache struct {
mu sync.RWMutex
data map[string]string
}
func (c *Cache) Get(key string) (string, bool) {
c.mu.RLock() // 读锁(多个读可并发)
defer c.mu.RUnlock()
val, ok := c.data[key]
return val, ok
}
func (c *Cache) Set(key, val string) {
c.mu.Lock() // 写锁(独占)
defer c.mu.Unlock()
c.data[key] = val
}
// ======== tryLock(Go 1.18+)=======
func tryLockExample() {
var mu sync.Mutex
// 非阻塞尝试获取锁
if mu.TryLock() {
// 成功获取锁
defer mu.Unlock()
// ... 执行需要锁的操作
} else {
// 锁已被占用,跳过或等待
fmt.Println("Lock busy, skipping")
}
}
// ======== Mutex 嵌套死锁 ========
// ❌ 错误:嵌套锁导致死锁
func deadlockExample() {
var mu1, mu2 sync.Mutex
// Goroutine A: mu1 → mu2
go func() {
mu1.Lock()
time.Sleep(100 * time.Millisecond)
mu2.Lock() // 等待 mu2
}()
// Goroutine B: mu2 → mu1
go func() {
mu2.Lock()
time.Sleep(100 * time.Millisecond)
mu1.Lock() // 等待 mu1 → 死锁
}()
time.Sleep(1 * time.Second)
// 死锁!两个 Goroutine 都无法继续
}
4.2 WaitGroup 与 errgroup
go
// ======== WaitGroup ========
func waitGroupExample() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1) // 在启动前 Add
go func(id int) {
defer wg.Done() // 在退出时 Done
time.Sleep(time.Duration(id) * time.Second)
fmt.Printf("Worker %d done\n", id)
}(i)
}
wg.Wait() // 等待所有 Goroutine 完成
fmt.Println("All workers done")
}
// ======== errgroup(错误收集)=======
func errgroupExample() {
g := errgroup.Group{}
results := make([]string, 3)
// 任务1
g.Go(func() error {
time.Sleep(1 * time.Second)
results[0] = "result1"
return nil
})
// 任务2
g.Go(func() error {
time.Sleep(2 * time.Second)
results[1] = "result2"
return nil
})
// 任务3(出错)
g.Go(func() error {
time.Sleep(500 * time.Millisecond)
return errors.New("task3 failed")
})
if err := g.Wait(); err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Results:", results)
}
// ======== errgroup.WithContext ========
func errgroupWithContext() {
g, ctx := errgroup.WithContext(context.Background())
g.SetLimit(3) // 限制并发数
for i := 0; i < 10; i++ {
g.Go(func() error {
select {
case <-ctx.Done():
return ctx.Err() // 其他任务出错,自动取消
default:
return processItem(i)
}
})
}
if err := g.Wait(); err != nil {
fmt.Println("Error:", err)
}
}
4.3 Once 与 sync.Pool
go
// ======== Once(单次执行)=======
type Singleton struct {
data string
}
var (
instance *Singleton
once sync.Once
)
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{data: "initialized"}
})
return instance
}
// ======== OnceValue/OnceValues(Go 1.21+)=======
var getConfig = sync.OnceValue(func() Config {
// 只执行一次
return loadConfig()
})
func main() {
config := getConfig() // 第一次调用:执行 loadConfig
config2 := getConfig() // 后续调用:返回缓存值
}
// ======== sync.Pool(对象池)=======
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer) // 创建新对象
},
}
func processWithPool() {
// 从池获取
buf := bufferPool.Get().(*bytes.Buffer)
defer bufferPool.Put(buf) // 放回池
buf.Reset()
buf.WriteString("hello")
fmt.Println(buf.String())
}
// ======== 连接池模式 ========
var connPool = sync.Pool{
New: func() interface{} {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
return nil
}
return conn
},
}
func getConnection() (net.Conn, error) {
conn := connPool.Get()
if conn == nil {
return nil, errors.New("no available connection")
}
return conn.(net.Conn), nil
}
func releaseConnection(conn net.Conn) {
connPool.Put(conn)
}
4.4 sync.Map
go
// ======== sync.Map(并发安全 Map)=======
// 适用场景:读多写少(如缓存、配置)
// 不适用场景:写多(性能不如 Mutex + map)
func syncMapExample() {
var m sync.Map
// 写入
m.Store("key1", "value1")
m.Store("key2", "value2")
m.Store("key3", 42)
// 读取
val, ok := m.Load("key1")
if ok {
fmt.Println(val) // "value1"
}
// 删除
m.Delete("key2")
// Range(遍历)
m.Range(func(key, val interface{}) bool {
fmt.Printf("%s: %v\n", key, val)
return true // true: 继续; false: 停止
})
// LoadOrStore(原子操作:读取或写入)
actual, loaded := m.LoadOrStore("key1", "new_value")
// loaded=true: 已存在,返回旧值
// loaded=false: 不存在,写入新值
fmt.Println(actual, loaded)
// LoadAndDelete(原子操作:读取并删除)
val, loaded := m.LoadAndDelete("key3")
// loaded=true: 存在并删除
}
// ======== 对比:Mutex + map vs sync.Map ========
// | 场景 | Mutex + map | sync.Map |
// |---------------|---------------|---------------|
// | 读多写少 | 较慢 | 快 |
// | 写多 | 快 | 较慢 |
// | 离散键 | 快 | 快 |
// | 相邻键 | 快 | 较慢(内部分段)|
// | 类型安全 | 是 | 否(interface{})|
// | 推荐 | 通用 | 读多写少场景 |
五、生产级 Worker Pool
5.1 Worker Pool 实现
go
// ======== 生产级 Worker Pool ========
package pool
import (
"context"
"errors"
"fmt"
"sync"
"sync/atomic"
)
// ======== Worker Pool 定义 ========
type WorkerPool struct {
workers int
taskCh chan Task
resultCh chan Result
errCh chan error
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
activeTask atomic.Int64
}
type Task struct {
ID int
Data interface{}
}
type Result struct {
ID int
Data interface{}
Error error
}
// ======== 创建 Worker Pool ========
func NewWorkerPool(ctx context.Context, workers int, queueSize int) *WorkerPool {
childCtx, cancel := context.WithCancel(ctx)
return &WorkerPool{
workers: workers,
taskCh: make(chan Task, queueSize),
resultCh: make(chan Result, queueSize),
errCh: make(chan error, 1),
ctx: childCtx,
cancel: cancel,
}
}
// ======== 启动 Worker Pool ========
func (p *WorkerPool) Start(processor func(ctx context.Context, task Task) (interface{}, error)) {
for i := 0; i < p.workers; i++ {
p.wg.Add(1)
go func(workerID int) {
defer p.wg.Done()
for {
select {
case <-p.ctx.Done():
return // Context 取消,Worker 退出
case task, ok := <-p.taskCh:
if !ok {
return // Channel 关闭,Worker 退出
}
p.activeTask.Add(1)
data, err := processor(p.ctx, task)
p.activeTask.Add(-1)
p.resultCh <- Result{
ID: task.ID,
Data: data,
Error: err,
}
}
}
}(i)
}
}
// ======== 提交任务 ========
func (p *WorkerPool) Submit(task Task) error {
select {
case p.taskCh <- task:
return nil
case <-p.ctx.Done():
return p.ctx.Err()
default:
return errors.New("queue full")
}
}
// ======== 等待完成 ========
func (p *WorkerPool) Wait() {
close(p.taskCh) // 关闭任务 Channel
p.wg.Wait() // 等待所有 Worker 完成
close(p.resultCh) // 关闭结果 Channel
close(p.errCh)
}
// ======== 取消 ========
func (p *WorkerPool) Cancel() {
p.cancel()
}
// ======== 获取结果 ========
func (p *WorkerPool) Results() <-chan Result {
return p.resultCh
}
// ======== 使用示例 ========
func main() {
ctx := context.Background()
pool := NewWorkerPool(ctx, 10, 100)
// 启动 Worker
pool.Start(func(ctx context.Context, task Task) (interface{}, error) {
// 处理任务
time.Sleep(100 * time.Millisecond) // 模拟工作
return fmt.Sprintf("result-%d", task.ID), nil
})
// 提交任务
for i := 0; i < 1000; i++ {
pool.Submit(Task{ID: i, Data: i})
}
// 等待完成
pool.Wait()
// 收集结果
for result := range pool.Results() {
if result.Error != nil {
fmt.Printf("Task %d error: %v\n", result.ID, result.Error)
} else {
fmt.Printf("Task %d: %v\n", result.ID, result.Data)
}
}
}
六、Checklist 总结
□ GMP 调度器
□ G(Goroutine)M(OS Thread)P(Processor)
□ Work Stealing(工作窃取)
□ GOMAXPROCS 调优
□ GOMEMLIMIT 内存限制
□ Goroutine 泄露检测
□ Channel 模式
□ 信号通知(done channel)
□ 单向 Channel(chan<- / <-chan)
□ 缓冲 Channel(0/1/N)
□ 关闭检测(v, ok := <-ch)
□ select 多路复用
□ 超时控制(time.After)
□ 非阻塞发送/接收(default)
□ 退出信号(close(quit))
□ Fan-out/Fan-in
□ Pipeline 流水线
□ Context
□ Background / TODO / WithCancel / WithTimeout / WithDeadline
□ WithValue(只传请求级别数据)
□ 链式组合
□ 取消传播
□ Goroutine 泄露预防
□ errgroup.WithContext
□ 并发原语
□ Mutex / RWMutex / tryLock
□ WaitGroup(Add/Done/Wait)
□ errgroup(Go/Wait/SetLimit)
□ Once / OnceValue / OnceValues
□ sync.Pool(对象池)
□ sync.Map(并发安全 Map)
□ Cond(条件变量)
□ Semaphore(信号量)
□ Worker Pool
□ 创建(workers + queueSize)
□ 启动(processor 函数)
□ 提交(Submit)
□ 等待(Wait)
□ 取消(Cancel + Context)
□ 结果收集(Results Channel)
□ 生产级注意事项
□ Goroutine 泄露预防(Context 传递)
□ 死锁预防(避免嵌套锁)
□ 竞态检测(go test -race)
□ CPU/IO 密集型并发限制
□ 锁粒度选择(粗锁 vs 细锁)
□ defer Unlock/Done
总结
并发原语选择矩阵:
| 需求 | 原语 | 说明 |
|---|---|---|
| 独占访问 | Mutex | 写操作互斥 |
| 读多写少 | RWMutex | 读可并发 |
| 等待完成 | WaitGroup | 等待 N 个 Goroutine |
| 错误收集 | errgroup | 任一出错取消所有 |
| 单次初始化 | Once | 只执行一次 |
| 对象复用 | sync.Pool | 减少 GC 压力 |
| 并发 Map | sync.Map | 读多写少场景 |
| Goroutine 通信 | Channel | CSP 模式 |
| 取消传播 | Context | 超时/手动取消 |
Channel vs Mutex 选择:
- Channel:Goroutine 间通信、数据传递、信号通知
- Mutex:共享内存访问、计数器、缓存更新
- 混合:Channel 传递信号 + Mutex 保护共享数据
性能调优关键参数:
GOMAXPROCS:CPU 密集 = CPU 核心数;IO 密集 = CPU * 2~4GOMEMLIMIT:容器 memory limit * 90%GCPercent:100(默认);内存敏感可降低到 50