Go 并发效率关键在于合理控制 GOMAXPROCS、阻塞操作、资源竞争和 goroutine 生命周期;盲目增加 goroutine 数量反而降低性能。

Go 的 goroutine 调度本身已高度优化,真正影响并发效率的往往不是“手动调度”,而是对 GOMAXPROCS、阻塞操作 、 共享资源竞争 和 goroutine 生命周期 的合理控制。盲目增加 goroutine 数量反而会因调度开销和内存压力降低性能。
合理设置 GOMAXPROCS 并理解其作用
GOMAXPROCS 控制的是可同时执行用户 Go 代码的 操作系统 线程数(即 P 的数量),默认等于 CPU 核心数。它不控制 goroutine 总数,也不决定“能开多少协程”,而是影响并行执行能力。
- CPU 密集型任务:保持默认(或略高于核心数)通常最优;设得过高会导致线程切换频繁,反而降低吞吐
- I/O 密集型任务:GOMAXPROCS 影响较小,但也不宜远低于核心数(否则部分 CPU 可能闲置)
- 避免运行时频繁修改:用
runtime.GOMAXPROCS(n)动态调整需谨慎,仅在启动时设置一次即可
减少 goroutine 阻塞与系统调用开销
goroutine 在遇到 I/O(如文件读写、网络请求)、锁等待、channel 阻塞或系统调用时会自动让出 P,由 runtime 调度其他 goroutine。但频繁或长时间阻塞仍会拖慢整体响应。
- 优先使用非阻塞 I/O:net/http、database/sql 等 标准库 默认支持异步底层(如 epoll/kqueue),无需额外操作
- 避免在 goroutine 中做同步 sleep 或轮询:改用
time.After、timer.Reset或事件驱动方式 - 慎用
runtime.LockOSThread:它会将 goroutine 绑定到 M,破坏调度灵活性,仅在需调用特定 OS 线程 API(如 OpenGL)时使用
避免 goroutine 泄漏与资源浪费
大量短命 goroutine 开销小,但若 goroutine 意外永不结束(如 channel 未关闭、select 缺少 default),就会持续占用 栈内存和调度器元数据,最终导致 OOM 或调度延迟升高。
立即学习“go 语言免费学习笔记(深入)”;
- 用 context 控制生命周期:为每个有依赖的 goroutine 传入
ctx,并在 select 中监听ctx.Done() - 关闭 channel 明确信号:向只读 channel 发送关闭通知,而非用 nil channel 或全局标志位
- 监控活跃 goroutine 数:开发期可用
runtime.NumGoroutine()日志或 pprof 查看异常增长
高效使用 channel 与同步原语
channel 是 Go 并发的核心,但不当使用会成为瓶颈。无缓冲 channel 的发送 / 接收是同步点,可能引发不必要的等待;互斥锁过度保护也会串行化逻辑。
- 根据场景选缓冲大小:生产者快于消费者时,用适当缓冲(如
make(chan T, 128))缓解阻塞;纯事件通知用无缓冲更清晰 - 避免大对象拷贝:channel 传递指针或轻量结构体,而非 megabytes 级数据
- 读写分离 + RWMutex:当读多写少时,用
sync.RWMutex替代sync.Mutex提升并发读性能






























