Golang中channel的使用问题求助

Golang中channel的使用问题求助 观察来自blog.golang.org/pipelines关于扇入扇出模式的代码,如果查看merge函数,你会发现它只是将传入的两个通道的值传递到另一个通道并返回该通道。我们向merge传递了两个具有相同值(4, 9)的通道,并获取一个返回通道。

当我们遍历这个新通道时,输出结果只能是4, 9或9, 4。

我的问题是: 为什么它没有打印另一组4, 9(为什么输出不是4,9,4,9)?为什么它只打印来自一个通道的值,而不是打印我们传递给merge函数的两个通道的所有值?

func main() {
 in := gen(2, 3)

// Distribute the sq work across two goroutines that both read from in.
c1 := sq(in)
c2 := sq(in)

// Consume the merged output from c1 and c2.
for n := range merge(c1, c2) {
    fmt.Println(n) // 4 then 9, or 9 then 4
}
}

func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
    for _, n := range nums {
        out <- n
    }
    close(out)
}()
return out
}

func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
    for n := range in {
        out <- n * n
    }
    close(out)
}()
return out
}

func merge(cs ...<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)

// Start an output goroutine for each input channel in cs.  output
// copies values from c to out until c is closed, then calls wg.Done.
output := func(c <-chan int) {
    for n := range c {
        out <- n
    }
    wg.Done()
}
wg.Add(len(cs))
for _, c := range cs {
    go output(c)
}

// Start a goroutine to close out once all the output goroutines are
// done.  This must start after the wg.Add call.
go func() {
    wg.Wait()
    close(out)
}()
return out
}

更多关于Golang中channel的使用问题求助的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

只有两个值被写入由 gen 返回的通道。当这两个值被读取后(第一个被 sq(in) 调用读取,第二个被另一个读取),该通道就会耗尽。通道并不会因为有两个 goroutine 从中读取而被复制。

请参阅 https://play.golang.com/p/Z3tjyOTSODn,我在你的代码中添加了一些输出。

更多关于Golang中channel的使用问题求助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言的扇入扇出模式中,merge函数的行为是正确的,它确实会输出所有传入通道的值。在你的示例中,输出结果应该是4,99,4,而不是4,9,4,9,这是因为每个输入通道(c1c2)只包含一个值。

让我们分析一下代码执行流程:

  1. gen(2, 3) 生成一个包含值 2 和 3 的通道
  2. sq(in) 被调用两次,创建两个独立的goroutine:
    • 第一个 sqin 读取 2,计算 2×2=4,发送到 c1
    • 第二个 sqin 读取 3,计算 3×3=9,发送到 c2
  3. 每个 sq 函数在发送完值后都会关闭各自的输出通道

所以 c1 只包含值 4,c2 只包含值 9。当 merge 函数合并这两个通道时,它会输出两个值:4 和 9,顺序取决于goroutine的调度。

如果你期望看到 4,9,4,9,你需要修改输入数据,让每个通道包含多个值。例如:

func main() {
    in := gen(2, 3, 4, 5) // 提供更多输入值
    
    c1 := sq(in)
    c2 := sq(in)
    
    for n := range merge(c1, c2) {
        fmt.Println(n) // 可能的输出: 4, 9, 16, 25 或其它顺序组合
    }
}

在这个修改后的版本中,每个 sq goroutine会从输入通道读取两个值:

  • c1 可能包含:4, 16
  • c2 可能包含:9, 25

merge 函数会合并所有四个值,输出顺序由goroutine调度决定,可能是:4, 9, 16, 25 或 4, 16, 9, 25 等不同组合。

merge 函数的工作原理是:

  • 为每个输入通道启动一个goroutine
  • 每个goroutine将其通道的所有值复制到输出通道
  • 当所有输入通道都关闭后,关闭输出通道

所以它确实会输出所有传入通道的所有值,只是在你原始的例子中,每个通道只有一个值。

回到顶部