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

huangapple go评论84阅读模式
英文:

Why can you reuse channels in Go when doing parallel processing?

问题

这是来自官方教程的代码片段:

package main

import "fmt"

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // 将 sum 发送到 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 // 从 c 接收

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

由于我们在并行计算中,每个线程都将其结果保存到同一个通道中,这样不会破坏数据。

英文:

Here is a code snippet from the official tutorial

package main

import &quot;fmt&quot;

func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c &lt;- 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 := &lt;-c, &lt;-c // receive from c

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

Since we are doing the calculation in parallel, and each thread saves its result into the same channel, doesn't this screw up the data?

答案1

得分: 1

当你从两个不同的goroutine通过通道发送两个值时,确实不能保证顺序(除非你做了其他协调它们发送的操作)。

然而,在这个例子中,顺序并不重要。在通道上发送了两个值:前半部分的和以及后半部分的和。

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

由于这两个值唯一用于计算总和,所以顺序并不重要。实际上,如果你多次运行这个例子,你应该会看到xy经常交换位置,但总和x+y始终相同。

英文:

It's true that when you send two values over a channel from two different goroutines that the ordering is not necessarily guaranteed (unless you've done something else to coordinate their sends).

However, in this example, the ordering doesn't matter at all. Two values are being sent on the channel: the sum of the first half and the sum of the second.

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

Since the only thing those two values are used for is to calculate the total sum, the order doesn't matter at all. In fact, if you ran the example enough times you should see that x and y are often swapped, but the sum x+y is always the same.

答案2

得分: 1

通道的操作是协程安全的。你可以在任何协程中进行读取、写入和关闭操作,而不会破坏通道中传入或传出的任何内容。基本上,通道是同步点。无缓冲通道(就像你的情况)在每次写入和读取时都会阻塞。当你写入时,代码会阻塞并等待直到另一端开始读取。当你读取时,代码会阻塞并等待直到另一端开始写入。

在你的情况下,协程中的计算将并发地进行(不一定是并行的),但会在通道写入时阻塞。你的主协程将在第一次读取时阻塞,读取值。然后在第二次读取时阻塞,读取值。

即使你使用了带缓冲的通道 - c := make(chan int, 2),你的协程也会完成计算,将结果写入通道而不会阻塞,并终止。没有任何内容会被破坏。与此同时,主协程将在通道读取时阻塞,并等待直到有人向其写入。

我建议你阅读《Effective Go》《Go Concurrency Patterns》并尝试《A Tour of Go》

英文:

Operations with channels are goroutine safe. You can read/write/close in any goroutine without corrupting anything that goes in or out of the channel. Basically, channels are synchronization points. Unbuffered channels (like in your case) will block on every write and read. When you write your code will block and wait until someone starts reading on the other end. When you read your code will block and wait until someone starts writing on the other end.

In your case calculations in goroutines will be done concurrently (not necessary in parallel) but will block on channel write. Your main goroutine will block on the first read, read the value. Block on the second read, read the value.

Even if you use a buffered channel - c := make(chan int, 2). Your goroutines will finish calculations, write resuls to the channel without blocking and terminate. Nothing will be corrupted. In the meantime main goroutine will block on channel read and wait until someone writes to it.

I suggest you read effective go, Go Concurrency Patterns and try A Tour of Go

huangapple
  • 本文由 发表于 2016年3月7日 07:11:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/35833793.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定