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

Posted

技术标签:

【中文标题】如果通过 Golang 通道发送,结构是不是实际上在 goroutine 之间复制?【英文标题】:Is a struct actually copied between goroutines if sent over a Golang channel?如果通过 Golang 通道发送,结构是否实际上在 goroutine 之间复制? 【发布时间】:2016-06-21 02:42:27 【问题描述】:

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

例如,在下面的代码中,Go 实际上会在 goroutines 生产者和消费者之间复制所有 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

【问题讨论】:

另外,如果结构包含一个切片,`[]int',则按值传递切片(以及结构)的效果不会复制内部数组。我并不是说这是你的答案。 【参考方案1】:

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

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

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

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

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

【讨论】:

这是两种类型频道的游乐场演示:play.golang.org/p/-VFWCgOnh0 @LucasJones 我会将您的示例添加到帖子中以提高知名度。 如果您只发送一个指针,请注意潜在的竞争条件。你不想让两个 goroutine 都弄乱同一个结构【参考方案2】:

The Go Programming Language Specification

Send statements

send 语句在通道上发送一个值。渠道表达 必须是通道类型,通道方向必须允许发送 操作,并且要发送的值的类型必须可分配给 频道的元素类型。

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

【讨论】:

以上是关于如果通过 Golang 通道发送,结构是不是实际上在 goroutine 之间复制?的主要内容,如果未能解决你的问题,请参考以下文章

[golang] channel通道

结构中的 Golang 通道:传递给函数时可以是定向的吗?

Golang通道的无阻塞读写的方法示例

golang fifo 缓冲通道

Golang 缓冲通道在发送之前接收数据

Golang:无法在恢复()中向通道发送错误