Golang GMP调度模型是怎样的_Golang调度器原理教程【核心】

张开发
2026/4/13 20:34:25 15 分钟阅读

分享文章

Golang GMP调度模型是怎样的_Golang调度器原理教程【核心】
GMP是动态协作契约G在M上运行M需持有P才能执行用户代码runtime.GOMAXPROCS控制P数量而非线程数设过高会导致调度开销增大、竞争加剧、GC变慢。GMP 不是固定绑定关系而是一套动态协作契约G 必须在 M 上跑M 必须持有一个 P 才能执行用户代码——这是调度成立的最小前提。为什么 runtime.GOMAXPROCS 设太高反而卡顿它控制的是 P 的数量不是线程数。每个 P 独立维护本地队列、timer 堆、mcache 等资源P 过多会导致? 调度器频繁在多个 P 间切换增加 findrunnable 开销? 全局队列和 work-stealing 竞争加剧尤其在高并发 channel 操作时更明显? GC 标记阶段需遍历所有 P 的栈和本地队列P 越多扫描越慢? 默认值就是 CPU 核心数除非你明确知道某段 I/O 密集型逻辑长期阻塞 M比如阻塞式 syscall否则别盲目调高go func() { ... } 创建的 goroutine 真的立刻执行了吗不。它只是被放入当前 P 的本地运行队列尾部runq.pushBack如果本地队列已满默认 256 个 G则入全局队列runqglo或触发偷任务stealWork。常见误解是“启动就跑”实际取决于? 当前 M 是否空闲刚完成上一个 G 或刚从阻塞恢复? 本地队列是否非空且轮到它? 是否有更高优先级的 timer 或 netpoll 事件待处理? 若此时发生系统调用M 会解绑 P其他 M 可能先抢走这个 P 去执行别的 Ggoroutine 阻塞时M 和 P 怎么拆伙又重组这是 GMP 区别于线程池的关键动作。当 G 调用 select、channel、net.Read 或 time.Sleep 时? G 状态变为 _Gwaiting脱离当前 M 的执行流? M 主动释放持有的 Phandoffp进入休眠或去偷任务? P 被其他空闲 M 获取继续调度其他 G? 阻塞结束如 channel 收到数据、timer 到时、epoll 返回就绪 fdG 被唤醒并重新入 runq本地或全局? 注意若 M 在阻塞期间被系统回收如长时间无任务P 会被放入空闲 P 链表下次需要时再复用——P 永远不会销毁但 M 可能被 OS 杀掉 稿定AI 拥有线稿上色优化、图片重绘、人物姿势检测、涂鸦完善等功能

更多文章