2017-07-24 142 views
-2

我有for循环与goroutines。我在循环中创建了打印一个字符串和“我”这是一个int的去例程。我知道字符串,“我”会按照随机顺序打印。但是,“我”没有正确添加,如下所示。五个字符串中的三个或四个字符串的值保持不变,然后跳转到2或1.不应该有随机顺序中的1,2,3,4,5个字符吗?我究竟做错了什么?Golang for循环加入i ++不一致

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 

回答

5

通过封闭件的所有够程共享相同的变量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,并从不同的够程没有任何同步写i。这是一场数据竞赛。在这种情况下可能发生任何事情。

围棋比赛探测器甚至骂你

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 
+0

谢谢你向我解释。我听说过一场比赛的情况,但直到现在还不明白。 –

5

但是也有一些发生在每一个够程几件事情:

  1. 它打印出的i的价值,因为它是当时打印语句执行
  2. 它递增i的值。

不能保证这两件事发生在原子上,或者它们按什么样的顺序发生。

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

  1. 使用互斥来保护访问i(但还挺失败有时paralellism点)
  2. i作为参数传递给你的功能(go func(i int){}(i))。 (像克罗姆的答案)
  3. 使用原子操作来交换我到一个新的地方在每个goroutine中并加一(有点棘手得到恰到好处)

我建议2.但是,我也建议反对使用goroutine命令作为随机的来源。

+0

谢谢。我真正的程序正在处理数千行,这就是为什么我想在我的for循环中执行例程以节省时间。克罗姆的例子为我工作。感谢您更好地解释这一点。 –