You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调整两个相同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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 02:10:30