Go Runtime(二):GMP与Goroutine调度
本文源码基线为 Go 1.26.2、Linux/amd64。调度器变化频繁;函数名、队列容量和分支顺序均以固定版本源码为准。 Go 调度器解决的核心问题是:怎样把大量 goroutine 映射到较少的 OS 线程,同时控制竞争、阻塞和调度延迟。 理解调度器不需要记住所有函数。先抓住三件事:执行资格属于 P,等待发生在 G 上,真正运行机器指令的是 M。 先观察:两个P为什么会有更多线程 下面的程序把 GOMAXPROCS 设为 2,同时制造 runnable G、Timer 和一个阻塞 syscall: GOpackage main import ( "runtime" "syscall" "time" ) func main() { runtime.GOMAXPROCS(2) for range 20 { go func() { for { runtime.Gosched() } }() } var pipe [2]int if err := syscall.Pipe(pipe[:]); err != nil { panic(err) } defer syscall.Close(pipe[0]) defer syscall.Close(pipe[1]) go func() { var b [1]byte _, _ = syscall.Read(pipe[0], b[:]) }() time.AfterFunc(500*time.Millisecond, func() { _, _ = syscall.Write(pipe[1], []byte{1}) }) time.Sleep(3 * time.Second) } 先构建再观察调度快照,避免把 go run 自身的编译进程混入输出: ...