You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang goroutine无法调度问题求助:死循环引发执行异常

问题根源:Go用户态调度器的调度触发机制

这是个非常典型的Go调度器行为问题,我来给你拆解清楚:

核心原因:无函数调用的死循环会霸占逻辑处理器(P)

Go采用的是M-P-G调度模型:

  • M:操作系统线程(OS Thread)
  • P:逻辑处理器,它是连接M和G的桥梁,每个P都绑定了一个本地goroutine队列,只有和P绑定的M才能执行goroutine
  • G:你的打印goroutine、死循环goroutine都属于G

Go的调度器是用户态调度,它需要特定触发条件才会切换goroutine,比如:

  • 函数调用(尤其是涉及runtime的函数)
  • 系统调用(比如IO操作)
  • goroutine时间片耗尽(默认10ms左右)

但你写的for {}这种死循环,里面没有任何函数调用,也不会触发系统调用,更不会主动让出CPU。当这个死循环goroutine被调度到某个P上执行时,它会一直霸占着M和P,调度器完全没有机会介入切换到其他goroutine(也就是你那个打印i的G)。

为什么i会停在某个随机数值?

程序启动初期,调度器会把两个goroutine(打印i的和死循环的)都放到P的队列里,调度器会交替调度它们执行。但当死循环的G被选中执行后,它就再也不会让出CPU了,所以打印i的G就再也得不到运行机会,终端也就停在了最后一次打印的i值(这个数值是随机的,取决于调度器切换的时机)。

加了fmt.Println("ABC")就正常的原因

fmt.Println内部最终会调用系统调用(比如向标准输出写数据的write系统调用)。当goroutine进入系统调用时,Go调度器会自动把当前的P和M解绑,然后把P分配给其他空闲的M,这样你的打印goroutine就能被其他M+P组合调度执行了。当系统调用完成后,死循环的G会重新尝试获取P,这样两个goroutine就能交替被调度,终端也就会交替输出ABC和i了。

怎么解决这个问题?

如果你需要保留死循环但又想让其他goroutine正常运行,可以在死循环里主动让出CPU:

for {
    runtime.Gosched() // 主动告诉调度器:我先让一让,你调度其他goroutine吧
}

或者避免这种无任何函数调用的空死循环,尽量在循环里加入一些会触发调度的操作(比如IO、sleep、或者调用runtime相关函数)。

内容的提问来源于stack exchange,提问作者Mann King

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:52:33