Go Goroutine调度机制及运行原理相关技术问询
关于Goroutine工作方式的基础疑问
参考代码
package main import ( "fmt" "time" ) type Country struct { Name string Continent string Population int32 } func updatePopulation(c *Country, newBorns int32) { c.Population += newBorns fmt.Printf("New population of %v is %v\n", c.Name, c.Population) } func main() { totalStates := 50 // total number of sources, (US states) us := Country{"USA", "North America", 32000000} for i := 0; i <= totalStates; i++ { go updatePopulation(&us, int32(i)) } time.Sleep(time.Second * 5) // this is just so that we don't need channels }
问题解答
1. 子Goroutine是否需要主Goroutine阻塞才会运行?
不是的。调用go关键字启动Goroutine时,Go运行时会立刻将该Goroutine加入调度队列,只要调度器有空闲的逻辑处理器(P),就会安排它执行。示例中的time.Sleep只是为了阻止主Goroutine提前退出——如果主Goroutine直接结束,整个程序会终止,所有未完成的子Goroutine都会被强制终止,和子Goroutine的启动时机无关。
2. 无阻塞Goroutine执行顺序混乱的原因?
首先需要纠正:Go从1.14版本开始默认启用抢占式调度,不再是单纯的协作式。即使Goroutine没有阻塞操作,调度器也会在Goroutine运行超过约10ms、或者遇到函数调用时进行抢占,切换到其他Goroutine执行。另外,当GOMAXPROCS值大于1时,多个Goroutine可以被调度到不同的OS线程上,在多CPU核心上真正并行执行,这会进一步导致执行顺序无规律——最终的执行顺序由Go运行时调度器和操作系统的线程调度共同决定,没有固定顺序。
3. Go Runtime的线程池是否针对每个程序实例?
是的。每个Go程序执行实例(进程)都会拥有独立的线程池(由OS线程M组成),这些线程用于绑定逻辑处理器(P),进而调度Goroutine。线程池的大小会动态调整:当有Goroutine因IO、系统调用等阻塞时,Runtime会创建新线程来保证其他Goroutine的调度;当阻塞解除后,多余的线程会被回收。不同程序实例的线程池相互独立,互不影响。
内容的提问来源于stack exchange,提问作者Ram Dittakavi
相关产品推荐
相关产品推荐

