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

Go语言通道赋值机制解析:两段代码输出差异原因咨询

Goroutine与通道赋值的行为差异分析

情况1:移除b = a时,输出为2

对应代码:

package main

import "fmt"

func main() {
    ch := make(chan int)
    a := ch
    b := ch

    a = make(chan int)
    go f(a, b)

    a = make(chan int)
    go f(a, b)

    a = make(chan int)
    go f(a, b)

    go func() { a <- 1 }()
    fmt.Println(<-ch)
}

func f(a, b chan int) {
    b <- 1 + <-a
}

运行逻辑拆解:

  • 初始状态:a和b都指向同一个通道ch。
  • 每次执行a = make(chan int)时,a会指向一个新创建的通道,但b始终保持指向最初的ch。
  • 三次启动的f goroutine中,b参数都是ch,而a参数分别是三个不同的新通道。
  • 最后启动的匿名goroutine,只会往第三个新通道里发送1。
  • 只有第三个f能收到这个1,计算1+1=2后往ch发送;另外两个f的a通道永远收不到值,会一直阻塞,但主程序不会等待它们,最终从ch读到的就是2。

情况2:添加b = a时,输出为4

对应代码:

package main

import "fmt"

func main() {
    ch := make(chan int)
    a := ch
    b := ch

    a = make(chan int)
    go f(a, b)
    b = a

    a = make(chan int)
    go f(a, b)
    b = a

    a = make(chan int)
    go f(a, b)
    b = a

    go func() { a <- 1 }()
    fmt.Println(<-ch)
}

func f(a, b chan int) {
    b <- 1 + <-a
}

运行逻辑拆解:

  • b = a的作用是更新b的指向,让它和当前的a指向同一个新通道,从而形成了一条通道链:
    1. 第一次操作:a指向新通道1,启动f(新通道1, ch);然后b改为指向新通道1。
    2. 第二次操作:a指向新通道2,启动f(新通道2, 新通道1);然后b改为指向新通道2。
    3. 第三次操作:a指向新通道3,启动f(新通道3, 新通道2);然后b改为指向新通道3。
  • 最后匿名goroutine往新通道3发送1,触发链式计算:
    • 第三个f收到1,计算1+1=2,往新通道2发送。
    • 第二个f收到2,计算2+1=3,往新通道1发送。
    • 第一个f收到3,计算3+1=4,往ch发送。
  • 主程序从ch读到的就是最终累加后的4。

总结b = a的具体行为

b = a是通道变量的赋值操作:在Go中,通道是引用类型,赋值只会复制通道的引用(即指向通道的指针),不会复制通道本身。每次执行b = a后,b就会和a指向同一个通道实例,这就把后续f的b参数和前一个f的a参数关联起来,形成了传递数据的链条,最终让初始的1经过三次1+的累加,传到了ch中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:43:24