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

Golang for循环,i+的添加不一致+;,go,Go,我和goroutines有个循环。我在循环中创建go例程,它打印一个字符串和一个int的“I”。我知道字符串和“I”将按随机顺序打印。但是,正如您在下面看到的,“i”并没有正确添加。五个字符串中的三个或四个的值保持不变,然后跳到2或1。不应该有一个1,2,3,4,5的随机顺序吗?我做错了什么 package main import ( "fmt" "sync" ) func main() { a := []string{ "apple",

我和goroutines有个循环。我在循环中创建go例程,它打印一个字符串和一个int的“I”。我知道字符串和“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
我的目标(随机顺序)


通过闭包,所有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()
}
通常情况下:在您发布的程序中,您正在从不同的goroutine读取
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

通过闭包,所有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()
}
通常情况下:在您发布的程序中,您正在从不同的goroutine读取
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

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

  • 它按执行print语句时的状态打印
    i
    的值
  • 它增加
    i
    的值 无法保证这两件事是以原子的方式发生的,也不能保证这两件事是以什么顺序发生的

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

  • 使用互斥锁来保护对
    i
    的访问(但这有时会破坏并行性)
  • i
    作为参数传递给函数(
    go func(i int){}(i)
    )。(就像克罗姆的回答)
  • 在每个goroutine中,使用原子操作将i交换到一个新的本地并增加它(要获得正确的结果有点棘手)

  • 我推荐两种。但我也建议不要使用goroutine顺序作为随机性的来源。

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

  • 它按执行print语句时的状态打印
    i
    的值
  • 它增加
    i
    的值 无法保证这两件事是以原子的方式发生的,也不能保证这两件事是以什么顺序发生的

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

  • 使用互斥锁来保护对
    i
    的访问(但这有时会破坏并行性)
  • i
    作为参数传递给函数(
    go func(i int){}(i)
    )。(就像克罗姆的回答)
  • 在每个goroutine中,使用原子操作将i交换到一个新的本地并增加它(要获得正确的结果有点棘手)

  • 我推荐两种。但我也建议不要使用goroutine顺序作为随机性的来源。

    谢谢。我真正的程序是处理数千行,这就是为什么我想在for循环中使用go例程来节省时间。克罗姆的例子对我很有用。谢谢你更好的解释,谢谢。我真正的程序是处理数千行,这就是为什么我想在for循环中使用go例程来节省时间。克罗姆的例子对我很有用。谢谢你更好的解释,谢谢你给我解释。我听说了一个比赛条件,但直到现在才明白。谢谢你向我解释。我听说过比赛的情况,但直到现在才明白。