Go 高并发进阶实战:GMP 调度模型 + Channel 模式 + Context 控制 + 并发原语 + 生产级模式

前言

💡 痛点: 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~4
  • GOMEMLIMIT:容器 memory limit * 90%
  • GCPercent:100(默认);内存敏感可降低到 50