golang缓冲通道意外结果

时间:2017-03-28 04:21:23

标签: go

package main
import (
    "time"
    "runtime"
)

var c = make(chan int, 2)

func main() {
  go worker(1)
  for i := 0; i < 30; i++ {
    go func() {
            // k := i   make a local copy not make any difference
            c <- i
    }()
  }
  time.Sleep(100* time.Second)
}

func worker(id int) {
  for {
    a := <-c
    println(a, runtime.NumGoroutine())
    time.Sleep(time.Second)
  }
}

输出是不可预测的,有时候如下所示。

7 9
13 29
13 28
13 27
13 26
13 25
13 24
13 23
16 22
16 21
17 20
19 19
21 18
21 17
23 16
25 15
26 14
26 13
26 12
26 11
26 10
26 9
26 8
26 7
27 6
27 5
13 4
28 3
30 2
30 2

我知道如果缓冲区通道已满,发送方将阻止,当通道可用时,发送方可以继续。

  1. 为什么输出不是常数输出0-29?如何制作??
  2. 如何在goroutine中存储变量/局部变量?
  3. 如果许多发件人被阻止,他们是否被FIFO命令唤醒?

2 个答案:

答案 0 :(得分:2)

输出不是常数,因为不同的goroutine共享相同的局部变量i。如果取消注释你的线并在goruoutine调用之前移动它,你会看到常量输出0-29。更好的方法是将i变量移动到goroutine函数参数。

规格中未指定唤醒订单。你应该把它视为一个随机的。

答案 1 :(得分:0)

3是FIFO

1因为在for循环中创建的goroutine不一定按顺序执行。底层的Go调度程序将随机启动一个(这是通道调度其值的方式)。当然所有这些都将被创建,但它们将(调度)从time.Sleep(...)中的main开始被调用(Go调度程序是一个合作的,并且在某些点上执行此操作,如函数电话,频道操作等 - 例如this

2直接使用频道:

var (
    c  = make(chan int, 2)
    wg = &sync.WaitGroup{}
)

func main() {
    wg.Add(1)
    go worker(1)

    wg.Add(1)
    go func() {
        defer wg.Done()
        for i := 0; i < 30; i++ {
            c <- i
        }
        close(c)
    }()
    wg.Wait()
}

func worker(id int) {
    defer wg.Done()
    for a := range c {
        println(a, runtime.NumGoroutine())
        time.Sleep(time.Second)
    }
}

关于传递for循环变量;你几乎正确地完成了它。你只需要在goroutine之外设置一行来创建局部闭包:

var (
    wg = &sync.WaitGroup{}
)

func main() {
    for i := 0; i < 3; i++ {
        localClosure := i // <- this line

        wg.Add(1)
        go func() {
            defer wg.Done()
            println(localClosure)
        }()
    }

    wg.Wait()
}