Golang for循环,添加了不一致的i ++

时间:2017-07-24 17:22:47

标签: go

我有与goroutines的循环。我在循环中创建了go例程,它打印了一个字符串,“i”是一个int。我知道字符串和“i”将以随机顺序打印。但是“i”没有正确添加,如下所示。对于五个字符串中的三个或四个,该值保持不变,然后跳转到2或1.不应该以随机顺序存在1,2,3,4,5?我做错了什么?

package main

import (
    "fmt"
    "sync"
)

func main() {
    a := []string{
        "apple",
        "orange",
        "grape",
        "peach",
        "lemon",
    }

    wg := sync.WaitGroup{}
    wg.Add(len(a))
    i := 0
    for _, v := range a {
        go func(a string) {
            fmt.Println(a, i)
            i++
            wg.Done()
        }(v)
    }
    wg.Wait()
}

结果1:

orange 0
apple 0
lemon 0
peach 2
grape 0

结果2:

lemon 0
grape 0
peach 0
apple 0
orange 1

我的目标(随机顺序)

lemon 2
grape 4
peach 1
apple 0
orange 3

2 个答案:

答案 0 :(得分:6)

通过闭包,所有goroutine共享相同的变量i。试试这个:

package main

import (
    "fmt"
    "sync"
)

func main() {
    a := []string{
        "apple",
        "orange",
        "grape",
        "peach",
        "lemon",
    }

    wg := sync.WaitGroup{}
    wg.Add(len(a))
    for i, v := range a {
        go func(a string, j int) {
            fmt.Println(a, j)
            wg.Done()
        }(v,j)
    }
    wg.Wait()
}

通常:在您发布的程序中,您正在阅读i并从不同的goroutine编写i而没有任何同步。这是一场数据竞赛。在那种情况下可能发生任何事情。

go race detector甚至会对你大喊大叫

go run -race test.go
apple 0
==================
WARNING: DATA RACE
Read at 0x00c420010268 by goroutine 7:
  main.main.func1()
      /home/erwo/test.go:22 +0x6d

Previous write at 0x00c420010268 by goroutine 6:
  main.main.func1()
      /home/erwo/test.go:23 +0x191

Goroutine 7 (running) created at:
  main.main()
      /home/erwo/test.go:25 +0x15f

Goroutine 6 (finished) created at:
  main.main()
      /home/erwo/test.go:25 +0x15f
==================
orange 1
==================
WARNING: DATA RACE
Read at 0x00c420010268 by goroutine 8:
  main.main.func1()
      /home/erwo/test.go:22 +0x6d

Previous write at 0x00c420010268 by goroutine 6:
  main.main.func1()
      /home/erwo/test.go:23 +0x191

Goroutine 8 (running) created at:
  main.main()
      /home/erwo/test.go:25 +0x15f

Goroutine 6 (finished) created at:
  main.main()
      /home/erwo/test.go:25 +0x15f
==================
peach 2
grape 2
lemon 4
Found 2 data race(s)
exit status 66

答案 1 :(得分:5)

每个goroutine都会发生一些事情:

  1. 在print语句执行时打印i的值,因为它是
  2. 它会增加i的值。
  3. 无法保证这两件事会以原子方式发生,或者以哪种顺序发生在哪些goroutines上。

    为了获得您想要的结果,您可以:

    1. 使用互斥锁来保护对i的访问权限(但有时候会失去并列论点)
    2. i作为参数传递给您的函数(go func(i int){}(i))。 (像Krom的回答)
    3. 使用原子操作将i交换到每个goroutine中的一个新局部并递增它(有点难以正确使用)
    4. 我建议2.但我也建议不要使用goroutine命令作为随机源。