在 Go 中 GOMAXPROCS 变量可以限制并发运的 Goroutine 数量,也就是控制的 P 的数量。
但在实际运行中内核线程 M 往往会超过 GOMAXPROCS 的限制,也就是内核线程并不受GOMAXPROCS 变量的限制。
blocking syscall 导致线程增多
在发生系统调用是,如果运行时间过长,比如超过 10ms 就可能导致新的内核线程产生。监视器 sysmon
会定期轮询 runtime.allgs
中所有的逻辑处理器 P。如果 P 上的执行的系统调用时间阻塞较长,触发内核线程 M 与 P 分离。在后续调度中会通过 runtime.startm
来唤醒或产生新的 M 来与 P 绑定。这样如果在阻塞系统调用很多的场景就会导致产生很多的内核线程。
例如通过 read() 函数读文件操作,就是典型的阻塞调用。
https://colobu.com/2020/12/20/threads-in-go-runtime/
https://xargin.com/shrink-go-threads/
https://github.com/golang/go/issues/14592
http://xiaorui.cc/archives/5171