Is a struct actually copied between goroutines if sent over a Golang channel?

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

Is a struct actually copied between goroutines if sent over a Golang channel?

问题

在Go语言中,如果通过通道传输一个大的结构体,它是否会在goroutine之间进行实际的复制呢?

例如,在下面的代码中,Go语言是否会在生产者和消费者之间实际复制所有的largeStruct数据?

package main

import (
	"fmt"
	"sync"
)

type largeStruct struct {
	buf [10000]int
}

func main() {
	ch := make(chan largeStruct)
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go consumer(wg, ch)
	go producer(wg, ch)
	wg.Wait()
}

func producer(wg *sync.WaitGroup, output chan<- largeStruct) {
	defer wg.Done()
	for i := 0; i < 5; i++ {
		fmt.Printf("producer: %d\n", i)
		output <- largeStruct{}
	}
	close(output)
}

func consumer(wg *sync.WaitGroup, input <-chan largeStruct) {
	defer wg.Done()
	i := 0
LOOP:
	for {
		select {
		case _, ok := <-input:
			if !ok {
				break LOOP
			}
			fmt.Printf("consumer: %d\n", i)
			i++
		}
	}
}

在Go语言中,通过通道传输的数据是通过复制的方式进行传递的。在这个例子中,largeStruct数据会在生产者和消费者之间进行复制。每次生产者发送一个largeStruct实例到通道时,消费者会从通道接收到一个复制的largeStruct实例。因此,每个goroutine都会拥有自己的largeStruct实例的副本。

请注意,这种复制是浅复制,只复制结构体的值,而不会复制指向的底层数据。因此,复制的largeStruct实例仍然共享相同的底层数据。

英文:

If a large struct is sent over a channel in Go, is it actually copied between goroutines?

For example, in the code below, will Go actually copy all largeStruct data between goroutines producer and consumer?

package main

import (
    &quot;fmt&quot;
 	&quot;sync&quot;
)

type largeStruct struct {
    buf [10000]int
}

func main() {
    ch := make(chan largeStruct)
	wg := &amp;sync.WaitGroup{}
	wg.Add(2)
	go consumer(wg, ch)
	go producer(wg, ch)
	wg.Wait()
}

func producer(wg *sync.WaitGroup, output chan&lt;- largeStruct) {
    defer wg.Done()
	for i := 0; i &lt; 5; i++ {
		fmt.Printf(&quot;producer: %d\n&quot;, i)
		output &lt;- largeStruct{}
	}
	close(output)
}

func consumer(wg *sync.WaitGroup, input &lt;-chan largeStruct) {
    defer wg.Done()
	i := 0
LOOP:
    for {
		select {
		case _, ok := &lt;-input:
			if !ok {
				break LOOP
			}
			fmt.Printf(&quot;consumer: %d\n&quot;, i)
			i++
		}
	}
}

Playground: http://play.golang.org/p/fawEQnSDwB

答案1

得分: 24

是的,在Go语言中,一切都是复制的,你可以通过将通道改为使用指针(即chan *largeStruct)来轻松解决这个问题。

// 示例:http://play.golang.org/p/CANxwt8s2B

正如你所看到的,在每种情况下,v.buf的指针是不同的,但是如果你将其改为chan *largeStruct,指针将会相同。

@LucasJones提供了一个更容易理解的示例:https://play.golang.org/p/-VFWCgOnh0

正如@nos指出的,如果在发送后的两个goroutine中都修改了值,可能会出现潜在的竞争问题。

英文:

Yes, everything is a copy in Go, you can easily work around that by changing the channel to use a pointer (aka chan *largeStruct).

// demo: http://play.golang.org/p/CANxwt8s2B

As you can see, the pointer to v.buf is different in each case, however if you change it to chan *largeStruct, the pointers will be the same.

@LucasJones provided a little easier to follow example: https://play.golang.org/p/-VFWCgOnh0

As @nos pointed out, there's a potential race if you modify the value in both goroutines after sending it.

答案2

得分: 8

《Go编程语言规范》

发送语句

发送语句在通道上发送一个值。通道表达式必须是通道类型,通道的方向必须允许发送操作,要发送的值的类型必须可以赋值给通道的元素类型。

这是一个副本,因为通过将值分配给通道的元素类型来将值发送到通道。如果值是一个结构体,则会复制该结构体。如果值是指向结构体的指针,则会复制指向结构体的指针。

英文:

> The Go Programming Language Specification
>
> Send statements
>
> A send statement sends a value on a channel. The channel expression
> must be of channel type, the channel direction must permit send
> operations, and the type of the value to be sent must be assignable to
> the channel's element type.

It's a copy because the value is sent to the channel by assignment to the channel's element type. If the value is a struct, then the struct is copied. If the value is a pointer to a struct, then the pointer to the struct is copied.

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

发表评论

匿名网友

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

确定