go 1.21+ 应优先使用 sync.semaphore 控制并发,它基于带权重信号量,支持上下文取消、无竞态、panic 安全;初始化用 semaphore.newweighted(int64(n)),acquire 前检查 ctx,release 必须在 goroutine 内 defer 执行。

用 sync.Semaphore 控制并发最稳妥
Go 1.21+ 直接用 sync.Semaphore 是首选,它基于带权重信号量,天然支持上下文取消、无竞态、不会因 panic 漏释放。手写 sync.Mutex + 计数器容易出错:漏 Unlock、panic 后没恢复、defer 里释放但忽略 ctx 取消。
-
semaphore.NewWeighted(int64(n))初始化时传入最大并发数,注意必须是int64类型 - 每个 goroutine 启动前调用
sem.Acquire(ctx, 1);返回 error(如ctx.Err())就跳过,别硬起 -
sem.Release(1)必须在 goroutine 内部最外层defer,不能放在主协程或函数返回后 - 若任务本身耗时长且需响应取消,把同一个
ctx传给业务逻辑,别只用在Acquire那一瞬
用带缓冲 chan struct{} 实现轻量控制
兼容旧版本 Go(make(chan struct{}, n) 是最常用手段。它本质是令牌桶:发送占位、接收归还,配对天然,比 mutex 更安全。
- 声明:
sem := make(chan struct{}, 3),缓冲大小即最大并发数 - 获取许可:
sem (阻塞直到有空位) - 释放许可:
(必须执行,否则 channel 卡死,后续所有 goroutine 永久阻塞) - 务必在 goroutine 内部
defer func() { ,别在主循环里统一收 - 坑:channel 容量固定,无法动态调整;goroutine panic 未 recover 会导致该 goroutine 永不执行
,整个信号量池“漏气”
别拿 runtime.GOMAXPROCS 当并发限制用
runtime.GOMAXPROCS 控制的是 OS 线程(M)数量,不是 goroutine 并发数。它影响调度器并行度,和你的 HTTP 请求、DB 查询这些业务并发完全无关。设成 1 不代表只能跑 1 个 goroutine,只是所有 goroutine 在单个 M 上协作调度;设成 100 也不代表你能同时发起 100 个 API 调用——下游服务照样会 503 或 dial tcp: too many open files。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
选多少并发数?看瓶颈,别拍脑袋
并发数不是越大越好,得对齐真实瓶颈:
- IO 密集型(HTTP/DB):估算目标服务吞吐上限 × 单次平均耗时。例如对方 API QPS 上限 300,平均延迟 150ms → 理论并发 ≈ 45,建议从 40–60 开始压测
- CPU 密集型(加解密、图像处理):一般不超过
runtime.NumCPU(),再多只会增加线程切换开销 - 混合型或不确定场景:先设 10,用
go tool pprof观察goroutines数量曲线和scheduler latency,再逐步上调 - 特别注意:文件句柄、数据库连接池、HTTP client 的
MaxIdleConns这些资源限制,往往比 goroutine 数更早成为瓶颈
真正难的不是写出让 goroutine 数量可控的代码,而是判断「这个 50 是怎么来的」——它得经得起压测,也得扛得住下游抖动。一个数字背后,要连着监控、日志、超时、重试和熔断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










