为什么在进行并行处理时可以在Go中重用通道?

时间:2016-03-06 23:11:47

标签: go

以下是official tutorial

的代码段
package main

import "fmt"

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to c
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c // receive from c

    fmt.Println(x, y, x+y)
}

由于我们并行进行计算,并且每个线程将其结果保存到同一个通道中,这不会搞砸数据吗?

2 个答案:

答案 0 :(得分:0)

确实,当您通过两个不同的goroutine在一个频道上发送两个值时,订购不一定得到保证(除非您已经做了其他事情来协调他们的发送)。

然而,在这个例子中,排序并不重要。通道上发送了两个值:前半部分和第二部分之和。

go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)

由于这两个值的唯一用途是计算总和,因此订单根本不重要。实际上,如果您运行的示例足够多次,您应该会看到xy经常被交换,但总和x+y始终是相同的。

答案 1 :(得分:0)

渠道运营是安全的。您可以在任何goroutine中读取/写入/关闭,而不会破坏进出通道的任何内容。基本上,通道是同步点。无缓冲通道(如您的情况)将在每次写入和读取时阻塞。当你编写代码时会阻塞并等到有人开始阅读另一端。当你读到你的代码时会阻塞并等到有人开始写另一端。

在你的情况下,goroutine中的计算将同时完成(不需要并行),但会在通道写入时阻塞。您的主要goroutine将在第一次读取时阻止,读取值。阻止第二次读取,读取值。

即使您使用缓冲频道 - c := make(chan int, 2)。您的goroutine将完成计算,将结果写入通道而不会阻塞并终止。什么都不会被破坏。与此同时,主要goroutine将阻止频道读取并等待有人写入它。

我建议您阅读effective goGo Concurrency Patterns并尝试A Tour of Go