使用golang频道

时间:2017-03-28 07:40:32

标签: go channel

我遇到了一个奇怪的问题。脚本如下。

package main

import (
    "fmt"
    "sync"
)

type Data struct {
    data []int
}

func main() {
    ws := 5
    ch := make(chan *Data, ws)
    var wg sync.WaitGroup
    for i := 0; i < ws; i++ {
        wg.Add(1)
        go func(wg *sync.WaitGroup, ch chan *Data) {
            defer wg.Done()
            for {
                char, ok := <-ch
                if !ok {
                    return
                }
                fmt.Printf("Get: %d\n", len(char.data))
            }
        }(&wg, ch)
    }
    var d Data
    ar := []int{1}
    for i := 0; i < ws; i++ {
        d.data = []int{}
        for j := 0; j < 1000; j++ {
            d.data = append(d.data, ar[0])
        }
        ch <- &d
        // time.Sleep(time.Second / 1000) // When this line is moved, a number of data by put and get becomes same.
        fmt.Printf("Put: %d\n", len(d.data))
    }
    close(ch)
    wg.Wait()
}

这是运行,预计会有以下结果。 “Put”和“Get”的数据数量相同。

Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000

但是,每次都无法得到这个结果。结果如下。 “Put”和“Get”的数据数量每次都不同。

尝试1

Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000

尝试2

Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 16
Put: 1000
Get: 0

尝试3

Get: 1000
Put: 1000
Put: 1000
Get: 1
Put: 1000
Get: 1000
Put: 1000
Get: 1
Put: 1000
Get: 1000

虽然在我的电脑上,“Put”和“Get”的数据数量每次都不同,在play.golang.org,两个数据的数量总是相同的。 https://play.golang.org/p/QFSuZmZk7d为什么?

如果脚本中使用了time.Sleep(time.Second / 1000),则两个数据的数量都相同。如果你知道这个问题,请你教我。非常感谢你的时间。

2 个答案:

答案 0 :(得分:0)

您观察到的是&#34;数据竞赛&#34;。

的示例

当您同时访问同一条数据(其中至少有一条是写入数据)时会发生这种情况。

每次都会引用相同的结构。接下来可能发生的是少数可能性之一:

  1. 在你突变它之前在频道的另一边阅读了它(&#34;预期&#34;场景)

  2. 你在阅读之前开始改变它。在这种情况下,接收器可以读取任意数量的Data.data项,从0到1000,具体取决于读取的确切时间。

  3. 该问题有多种解决方案:

    1. 您可以在每次迭代时创建Data的新实例。为此,只需在循环体内移动var d Data声明即可。在这种情况下,每次迭代都会创建一个新结构,因此您可能不会错误地改变前一个结构。

    2. 您可以声明Data的频道(结构,而不是指向结构的指针):chan Data。在这种情况下,每次将Data实例发送到通道时都会隐式复制{{1}}实例(因为Go中的所有内容都按值传递,在分配时复制)。

答案 1 :(得分:0)

package main

import (
    "fmt"
    "sync"
)

/*
    信号量的考察,put 之后,必须等待 get 拿到之后才能推出循环
*/

type Data struct {
    data []int
}

func main() {
    ws := 5
    ch := make(chan *Data, ws)

    sem := make(chan bool)

    var wg sync.WaitGroup

    for i := 0; i < ws; i++ {
        wg.Add(1)

        go func(wg *sync.WaitGroup, ch chan *Data) {
            defer wg.Done()

            for {
                char, ok := <-ch
                if !ok {
                    return
                }
                fmt.Printf("Get: %d\n", len(char.data))
                sem <- true
            }
        }(&wg, ch)
    }

    var d Data
    ar := []int{1}

    // ws = 5
    for i := 0; i < ws; i++ {
        d.data = []int{}

        for j := 0; j < 1000; j++ {
            d.data = append(d.data, ar[0])
        }

        ch <- &d

        fmt.Printf("Put: %d\n", len(d.data))

        <-sem // 一个信号量,必须等待 get 完成之后才能继续put
    }
    close(ch)

    wg.Wait()
}