调整两个相同Go协程的启动顺序为何返回不同结果?
为何调整两个Go协程的启动顺序,运行结果会变化?
核心原因:通道接收的竞争特性
你代码里两个协程共用了同一个ticker.C通道,而Go的通道接收是竞争式的——每次ticker触发时,只会有一个协程能抢到这个事件并处理。哪个协程能抢到,取决于Go调度器的实时调度状态,而协程的启动顺序会影响调度器对它们的初始调度优先级,所以调整启动顺序后,每次抢事件的“赢家”概率会变化,最终运行结果也就不同。
代码里的额外逻辑漏洞
除了竞争问题,你的代码还有两处逻辑问题:
- 第二个协程监听的
sdone通道,主函数全程没有发送任何值,所以它永远不会输出Done second; - 主函数
ticker.Stop()后,给done发了两次值,但只有第一个协程在监听done,第二个协程完全不会响应这个结束信号,会一直阻塞在select中(不过主函数退出后所有协程会被强制终止,不会造成泄漏,但逻辑上不完整)。
修正方案
如果想让两个协程都能响应每次tick事件,应该给每个协程分配独立的ticker,或者使用广播机制。比如修改后的代码:
func main() { ticker1 := time.NewTicker(1 * time.Second) ticker2 := time.NewTicker(1 * time.Second) done := make(chan bool) sdone := make(chan bool) go func() { for { select { case <-done: fmt.Println("Done one") return case <-ticker1.C: fmt.Println("Running one") } } }() go func() { for { select { case <-sdone: fmt.Println("Done second") return case <-ticker2.C: fmt.Println("running second") } } }() time.Sleep(5 * time.Second) ticker1.Stop() ticker2.Stop() done <- true sdone <- true fmt.Println("Tickers stopped") }
这样每个协程有自己的ticker,每次tick都会各自触发输出,启动顺序不再影响结果。
内容的提问来源于stack exchange,提问作者Cristian
相关产品推荐
相关产品推荐

