如果通过 Golang 通道发送,结构实际上是否在 goroutine 之间复制?

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

如果在 Go 中通过通道发送大型结构,它是否真的在 goroutine 之间复制?

例如,在下面的代码中,Go 实际上会在 goroutines producer 和 consumer 之间复制所有 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++
        }
    }
}

游乐场:http://play.golang.org/p/fawEQnSDwB

是的,在 Go 中一切都是副本,您可以通过更改通道以使用指针(又名 chan *largeStruct)轻松解决这个问题。

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

如您所见,指向 v.buf 的指针在每种情况下都不同,但是如果将其更改为 chan *largeStruct,指针将相同。

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

正如@nos 所指出的,如果您在发送后修改两个 goroutine 中的值,则可能存在竞争。

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.

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