【问题标题】:confusing output when iterate golang unbuffered channel迭代golang无缓冲通道时输出混乱
【发布时间】:2020-08-09 14:04:38
【问题描述】:

我在迭代 golang 无缓冲通道时遇到了令人困惑的输出。 频道定义为chan []int。 然后我将两个切片推送到通道,[0 1] 和 [2 3]。但是当我从频道中获取元素时,我得到了 [2 3] 和 [2 3]。为什么会这样?

package main

import "fmt"
import "sync"

func producer(jobs chan []int, wg *sync.WaitGroup) {
    defer wg.Done()

    a := make([]int, 2)
    index := 0
    for i := 0; i < 4; i++ {
        a[index] = i
        index++
        if index == 2 {
            index = 0
            fmt.Printf("a: %+v\n", a)
            jobs <- a
        }    
    }

    close(jobs)
}

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    jobs := make(chan []int, 2)
    go producer(jobs, &wg)
    for job := range jobs {
        fmt.Printf("job: %+v\n", job)
    }

    wg.Wait()
}

预期输出:

a: [0 1]
a: [2 3]
job: [0 1]
job: [2 3]

实际输出:

a: [0 1]
a: [2 3]
job: [2 3]
job: [2 3]

【问题讨论】:

  • jobs &lt;- a之后添加a = make([]int, 2)
  • 此代码编译错误,我认为需要在第 29 行进行修改(从go producer(ch, &amp;wg)go producer(jobs, &amp;wg)

标签: go slice channel


【解决方案1】:

切片包含一个指向后备数组的指针。当您通过通道发送切片时,您发送的是对该支持数组的引用,因此在接收端,即使您多次读取切片,您实际上也引用了同一个共享支持数组。

您可以为每次迭代创建一个新切片并将其发送。每个切片都有一个单独的后备数组,并且将按您的预期工作。

【讨论】:

    【解决方案2】:

    稍微修改您的程序以便更好地阅读。

    package main
    
    import "fmt"
    import "sync"
    
    func producer(jobs chan []int, wg *sync.WaitGroup) {
        defer wg.Done()
    
        a := make([]int, 2)
        a[0] = 1
        a[1] = 2
                jobs <- a   //We are passing memory location of slice ( is nature of slice ), so the values changing next line will affect here too
        a[0] = 2
        a[1] = 3
               jobs <- a  
        
    
        close(jobs)
    }
    
    func main() {
        var wg sync.WaitGroup
        wg.Add(1)
        jobs := make(chan []int, 2)
        go producer(jobs, &wg)
        for job := range jobs {
            fmt.Printf("job: %+v\n", job)
        }
    
        wg.Wait()
    }
    

    我用 Array 尝试过相同的程序,然后我们将得到您期望的结果,请参见下面的代码

    package main
    
    import "fmt"
    import "sync"
    
    func producer(jobs chan [2]int, wg *sync.WaitGroup) {
        defer wg.Done()
    
       var a[2]int
        a[0] = 1
        a[1] = 2
                jobs <- a   
        a[0] = 2
        a[1] = 3
               jobs <- a  
        
    
        close(jobs)
    }
    
    func main() {
        var wg sync.WaitGroup
        wg.Add(1)
        jobs := make(chan [2]int)
        go producer(jobs, &wg)
        for job := range jobs {
            fmt.Printf("job: %+v\n", job)
        }
    
        wg.Wait()
    }
    

    Playground Link - Program using Slice

    Playground Link - Program using Array

    基本规则是切片通过引用传递

    【讨论】:

      猜你喜欢
      • 2020-09-03
      • 2016-08-30
      • 1970-01-01
      • 2018-07-03
      • 2019-10-26
      • 1970-01-01
      • 2014-09-26
      • 2016-06-20
      • 2016-10-08
      相关资源
      最近更新 更多