深入理解 GMP 调度模型
Go 运行时调度器将 goroutine 映射到 OS 线程上执行,GMP 代表 G(Goroutine)、M(Machine/OS 线程)、P(Processor,执行上下文)。P 的数量默认等于 CPU 核心数,可以通过 GOMAXPROCS 调整,这是 Go 并发性能的关键参数。
goroutine 的生命周期与状态
goroutine 有多个状态:waiting(阻塞在 channel 或系统调用)、runnable(就绪等待调度)、running(正在执行)。理解这些状态有助于分析死锁和性能问题。
channel 的底层实现
channel 内部有环形队列和两个等待队列(sendq 和 recvq)。向已满的无缓冲 channel 发送会阻塞,goroutine 被挂在 sendq 上;接收时会从 recvq 唤醒一个 sender 或从队列取数据。
常见并发陷阱
- 向已关闭的 channel 发送数据会 panic,但接收会返回零值
- 多个 goroutine 同时等待同一个 channel,只有 1 个会被唤醒——用 sync.WaitGroup 而非 channel 控制退出
- for range channel 会一直阻塞直到 channel 关闭
- context.WithCancel 是优雅退出多个 goroutine 的标准方式
小结
理解 GMP 调度和 channel 底层原理,才能真正写出高效的并发代码。遇到性能问题时,用 runtime.NumGoroutine() 和 pprof 分析 goroutine 的状态分布是第一步。
发表回复