Skip to content

Goroutine 与 GMP

goroutine 轻量线程、GMP 调度模型、抢占、泄漏。

Updated View as Markdown
For humans

Goroutine 与 GMP

goroutine 是 Go 并发的核心:几 KB 起步的轻量线程,十万级无压力。面试必考 GMP 模型。

goroutine 为什么轻

维度 goroutine 线程
2KB 起步,动态增长 MB 级固定
创建开销 ~1-2µs 数十 µs(内核调用)
切换 用户态(自己调度) 内核态
规模 十万级 千级

栈动态增长是关键:初始 2KB,按需增长(最大 1GB),goroutine 少时内存占用极小。

GMP 模型

GMP: G 是任务, M 是执行者, P 是调度器

角色 含义
G(Goroutine) 用户态任务:栈 + 上下文
M(Machine) 内核线程,真正执行 G
P(Processor) 调度器:本地 G 队列,默认 = CPU 核数

调度要点:

  • P 的数量 = GOMAXPROCS(默认 CPU 核数),每 P 一个本地队列
  • 创建 G 进本地队列;队列满进全局队列
  • work stealing:P 空闲时从别的 P 偷一半 G(负载均衡)
  • 阻塞系统调用:M 让出 P(P 挂到别的 M 上继续调度),G 等系统调用返回
  • 抢占:Go 1.14+ 异步抢占(信号),长时间运行的 G 会被强制让出(10ms 级),防止饿死其他 G

goroutine 泄漏 ★

goroutine 泄漏:G 永远阻塞,栈和资源不释放,积累导致内存耗尽。常见场景:

场景 解法
从 channel 读,无人写 超时(select + time.After)
向无缓冲 channel 写,无人读 带缓冲或超时
死循环无退出条件 检查退出标志(Context)
忘记关 channel 确定生产者/消费者职责
select {
case v := <-ch:
    // 正常
case <-time.After(3 * time.Second):
    // 超时退出, 不泄漏
}

面试话术:goroutine 泄漏 = 阻塞没出口。排查:pprof goroutine profile 看堆积的函数(见标准库篇)。

面试追问

  1. GMP 是什么? G 任务、M 内核线程、P 调度器(本地队列)。P 数 = CPU 核数
  2. goroutine 为什么轻? 栈 2KB 动态增长 + 用户态调度(无内核切换)。十万级无压力
  3. work stealing? P 空闲时偷别的 P 的 G,负载均衡。配合全局队列
  4. 怎么抢占? 1.14+ 异步抢占(信号),长时间运行的 G 强制让出。防饿死
  5. goroutine 泄漏怎么排查? 阻塞没出口(channel 等无人、死循环)。pprof goroutine profile 定位
Navigation

Type to search…

↑↓ navigate↵ selectEsc close