以下代码将永远运行,而不是在开始后一秒钟停止。带有无限循环的go例程似乎阻止了另一个例程发送到超时通道。那正常吗?
func main(){ timeout:=make(chan int) go func(){ time.SLeep(time.Second) timeout<-1 }() res:=make(chan int) go func(){ for{ } res<-1 }() select{ case<-timeout: fmt.Println("timeout") case<-res: fmt.Println("res") } }
简短的回答:是的。
当前的实现在goroutines之间使用协作调度。这意味着goroutine必须将执行移交给调度程序,以便另一个goroutine运行。将来有希望使用不会有此限制的抢占式调度程序。
发生以下任何一种情况(可能不是完整的列表)时,Goroutine会屈服于调度程序:
最后一个允许您在处理器非常密集的循环中手动让步给调度程序。我从来没有发现过需要它的原因,因为我所使用的几乎所有东西都具有足够的通信(通道或系统io),以至于我的程序永远不会卡住。
您可能还会听到GOMAXPROCS,它是对此的解决方案。尽管可以通过将所有goroutine置于不同的线程中来运行它们,但垃圾收集器最终将尝试运行并停止世界。当它停止运行时,不允许运行goroutine,并且如果高cpugoroutine永不屈服,则GC将永远阻止goroutine,但永远不会运行。