I have for loop with goroutines. I create the go routines in the loop which prints a string and "i" which is an int. I know the strings and "i" will print in random order which it does. But "i" is not adding correctly as you can see below. The value stays the same for three or four of the five strings then jumps to 2 or 1. Shouldn't there be a 1, 2, 3, 4, 5 in the random order? What am I doing wrong?
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()
}
Result 1:
orange 0
apple 0
lemon 0
peach 2
grape 0
Result 2:
lemon 0
grape 0
peach 0
apple 0
orange 1
My Goal (random order)
lemon 2
grape 4
peach 1
apple 0
orange 3
Through the closures all the goroutines share the same variable i
. Try that instead:
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()
}
Generally: In the program you posted you are reading i
and writing i
from different goroutines without any synchronisation. That is a data race. Anything could happen in that scenario.
The go race detector even yells at you
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
There are several things that are happening in each goroutine:
i
as it is at the time the print statement executes.i
.There is no guarantee that those two things happen atomically, or in what order they will happen by which goroutines.
In order to get your desired result, you can:
i
(but that kinda defeats the point of paralellism sometimes)i
as an argument to your function (go func(i int){}(i)
). (like Krom's answer)I'd recommend 2. But I would also advise against using goroutine order as a source of randomness.