最直接的并发数控制方式是用带缓冲的 chan struct{} 模拟信号量:初始化 sem := make(chan struct{}, maxWorkers),启动任务前需写入 sem

用 chan struct{} 实现轻量信号量控制并发数
最直接、无依赖、runtime 级别安全的控制方式,就是用带缓冲的 chan struct{} 模拟信号量。它不涉及锁、不触发 GC、也不依赖第三方库,本质是让 goroutine 在进入临界区前“抢令牌”,抢不到就阻塞在 channel 发送上。
- 初始化:
sem := make(chan struct{}, maxWorkers),maxWorkers是你允许同时执行的最大数量 - 启动任务前必须写入:
sem (若缓冲已满则阻塞) - 任务结束后必须读出:
(推荐放在 <code>defer里,避免 panic 导致漏释放) - 别用
len(sem)判断可用槽位——它返回的是当前缓冲中元素个数,不是“还能塞几个”;真正可用数需靠 channel 阻塞行为体现 - 常见错误:用
chan int当计数器、或误调close(sem)导致后续发送 panic
为什么不能靠 runtime.GOMAXPROCS 控制 goroutine 并发数
runtime.GOMAXPROCS 控制的是 P(Processor)数量,即**可并行执行用户代码的操作系统线程上限**,和“开了多少 goroutine”完全无关。设成 1,你照样能起 10 万个 goroutine,它们只是排队在一个 P 上快速切换,无法并行利用多核。
- CPU 密集型任务:GOMAXPROCS 过高(如设为 100)会增加线程切换开销,实测在
4–16范围内更稳 - I/O 密集型任务:GOMAXPROCS 影响较小,但也不建议远低于 CPU 核数(否则部分核闲置)
- 容器环境必须显式设置:
runtime.GOMAXPROCS(runtime.NumCPU())不可靠——它读的是宿主机核数;应从cgroups或环境变量取配额值 - 运行时动态修改极不推荐:仅在程序启动时设一次,后续勿再调用
HTTP Handler 里盲目启 goroutine 是性能毒药
每个请求都 go handler() 听起来很“Go 风格”,但实际极易引发 goroutine 泄漏、文件描述符耗尽、调度器卡顿。尤其当后端依赖慢、超时未设、channel 未关闭时,这些 goroutine 就变成幽灵,永远等不到结束。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 正确做法:用
errgroup.Group替代裸go+sync.WaitGroup,它天然支持 context 取消与错误传播 - 所有 I/O 操作必须带超时:
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second),然后传给http.Client.Do或数据库查询 - 避免闭包捕获大对象:像
for _, item := range items { go func() { use(item) }() }会导致所有 goroutine 共享最后一个item值,且阻止栈收缩 - 高频小任务别每请求起 goroutine:改用预热好的 worker pool,例如固定 20 个长期运行的 goroutine 消费任务 channel
goroutine 数持续上涨?先查泄漏再调参
线上 runtime.NumGoroutine() 长期 >5k 且不收敛,基本不是配置问题,而是泄漏。pprof 抓到的堆栈里若反复出现相同函数名(比如卡在某个 channel receive 或 time.Sleep),就是典型信号。
立即学习“go语言免费学习笔记(深入)”;
- 必查点:是否监听了未关闭的 channel?
select里有没有漏掉ctx.Done()分支? - 是否用了无缓冲 channel 做异步通知,但接收方已退出,导致发送方永久阻塞?
- 是否在 goroutine 内部做无限轮询(比如
for { select { case )?这会让调度器疯狂唤醒它 - GC 压力也会伪装成 goroutine 暴涨:频繁分配
[]byte或strings.Builder会推高 GC 频率,间接拖慢调度——此时该用sync.Pool复用缓冲区
真正难处理的从来不是“怎么限”,而是“为什么没退”。盯住 pprof 的 goroutine profile 和 trace 的 blocking 时间段,比调 GOMAXPROCS 有用十倍。


















