英文:
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 (
"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++
}
}
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论