I am trying to initialize an array of items in concurrently manner using go routines. However, the strange memory behavior of Go prevents me from doing so even I used the suggested primitives (channel). Below is the minimal reproduce:
func TestSliceInit(t *testing.T){
toInit := make([]int, 10)
syncLock := make(chan bool)
for i := range toInit{
go func(){toInit[i] = i; syncLock <- true}()
}
for range toInit{
<-syncLock
}
for i := range toInit{
if toInit[i] != i{
t.Error("fail to init")
}
}
}
The code is supposed to initialize the toInit
array to 0-9 but it does not. Instead, error will be produced. I tried this code on Goland 2018.1
Because the code is run concurrently, your variable i
is modified each time a goroutine is called because they all keep a reference to the same variable i
To prevent that to happen, pass the index parameter to your anonymous function.
package main
import "fmt"
func main() {
toInit := make([]int, 10)
syncLock := make(chan bool)
for i := range toInit{
go func(i int){
toInit[i] = i;
syncLock <- true}(i)
}
for range toInit{
<-syncLock
}
for i := range toInit{
if toInit[i] != i{
fmt.Println("error")
}
}
}
The other way would be to use a declaration style using the following
for i := range toInit{
i := i
go func(){
toInit[i] = i;
syncLock <- true}()
}
Here is a nice documentation about closures and goroutines