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

关于fmt.Println与goroutine打印顺序不一致的问题咨询

问题分析:为什么true会出现在输出中间而非末尾?

这个问题的核心在于goroutine的并发调度特性,以及不同goroutine之间打印操作的无同步执行,咱们一步步拆解:

1. 代码里的并发逻辑本质

你启动了和CPU核心数相同的工作goroutine,每个goroutine内部是按顺序执行的:先循环打印i*i,完成循环后往messages channel发送true。同时还有一个独立的goroutine在监听messages,收到消息就打印true。

但这里的关键是:多个工作goroutine之间是并行运行的,接收消息的goroutine也和它们并发执行。

举个实际场景:假设你有4个CPU核心,4个工作goroutine同时跑。其中goroutine B可能先完成了1000次循环,立刻发送true;而goroutine A这时候还在打印98xxx的平方值。接收消息的goroutine收到true后会马上打印,这时候goroutine A还在继续打印后续的平方——这两个打印操作(A的fmt.Println(i*i)和接收goroutine的fmt.Println(true))之间没有任何同步约束,Go调度器可以随时切换这两个goroutine的执行权,所以true就会插在A的打印输出中间。

2. 关于fmt.Println锁的误区

你可能会疑惑:fmt.Println不是有内部锁吗?为什么还会出现顺序混乱?

没错,fmt.Println的内部锁只是保证单个fmt.Println的输出不会被其他fmt.Println的输出拆分(比如不会出现半拉数字加半拉true的乱码),但它完全不保证不同fmt.Println调用之间的执行顺序。锁只解决了输出的原子性问题,没解决并发场景下的顺序问题。

3. 如何让true始终出现在最后?

如果想让所有平方值都打印完毕后,再打印所有true,只需要调整代码的执行顺序:先等所有工作goroutine完成打印,再处理messages的接收和打印。

修改后的代码如下:

package main
import (
 "fmt"
 "runtime"
 "sync"
)
func main() {
 // 给channel加缓冲,避免goroutine阻塞
 messages := make(chan bool, runtime.NumCPU())
 var wg sync.WaitGroup
 var x = 1000
 wg.Add(runtime.NumCPU())
 for i := 0; i < runtime.NumCPU(); i++ {
  go func(x int) {
   defer wg.Done()
   var i = 0
   for i < x {
    i += 1
    fmt.Println(i * i)
   }
   messages <- true
  }(x)
 }
 // 先等待所有工作goroutine完成所有打印操作
 wg.Wait()
 // 关闭channel,告诉接收方没有新消息了
 close(messages)
 // 现在再接收并打印所有true
 for i := range messages {
  fmt.Println(i)
 }
}

这样调整后,所有工作goroutine的打印任务全部完成后,才会开始打印true,顺序就完全符合你的预期了。

内容的提问来源于stack exchange,提问作者Jal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:36:00