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

Go语言用range遍历通道为何未接收全部值?代码问题解析

Go通道并发行为解析:为何90的平方未按预期输出?

以下代码是《The Go Programming Language》一书8.4节程序的修改版本:

package main

import (
    "fmt"
)

func main() {
    naturals := make(chan int)
    squares := make(chan int)
    done := make(chan struct{})

    // counter
    go func() {
        for x := 0; x <= 10; x++ {
            naturals <- x
        }
        done <- struct{}{}
    }()
    
    // squarer
    go func() {
        for x := range naturals {
            squares <- x * x
        }
        close(squares)
    }()
    
    // printer
    go func() {
        for x := range squares {
            fmt.Println(x)
        }
    }()
    
    <-done
    naturals <- 90
    
    close(naturals)
}

运行上述代码时,输出如下:

0
1
4
9
16
25
36
49
64
81
100

将代码最后几行修改为如下内容后:

naturals <- 90
<-done

输出变为:

8100
0
1
4
9
16
25
36
49
64
81

现象原因解析

要搞懂这个问题,必须抓住Go并发的几个核心规则:

  • 无缓冲通道是同步的:发送方必须等接收方准备好才能完成发送,接收方也必须等发送方准备好才能完成接收。
  • main goroutine退出即终止所有goroutine:只要main函数执行完,后台的goroutine不管有没有干完都会被强制停掉。
  • for range读取通道的逻辑:会一直从通道取数据,直到通道被关闭才会退出循环。

第一种情况(无8100输出)

代码最后执行顺序是:<-done → naturals <-90 → close(naturals)

  1. <-done会阻塞main,直到counter goroutine把0-10全发送到naturals,然后向done发信号。此时counter已经退出。
  2. main向naturals发90:无缓冲通道要求squarer必须接收,squarer此时在range naturals循环里,会拿到90,算出8100并发送到squares。
  3. main关闭naturals:squarer的range循环会因为通道关闭而退出,接着关闭squares。
  4. 关键问题:main在关闭naturals后直接退出了!printer goroutine此时刚收到8100还没来得及打印,就被main退出带来的强制终止给打断了,所以8100没出现在输出里。

而0-10的平方能全部输出,是因为counter发送这些值时,squarer和printer是同步处理的:counter发一个数,squarer接收计算后发往squares,printer立刻接收打印,整个流程在counter发下一个数前就完成了,所以这些值都能在counter发done信号前打印完毕。

第二种情况(所有值都输出)

代码最后执行顺序是:naturals <-90 → <-done

  1. main先向naturals发90:无缓冲通道下,squarer如果已经在等待接收,就会先拿到90,算出8100发往squares,printer同步接收并打印8100。
  2. main开始等待<-done:这期间counter会持续发送0-10到naturals,squarer依次接收计算,printer同步打印这些平方值,直到counter发完所有数并向done发信号。
  3. main收到done信号后退出,此时所有该打印的值都已经被printer处理完毕,所以输出包含所有结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 16:25:27