1

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

Ruomu
  • 27
  • 3
  • 1
    https://golang.org/doc/faq#closures_and_goroutines – Peter Aug 11 '18 at 04:12
  • BTW you don't need syncing when no two goroutines update the same array element. IOW it is safe to concurrently init diffirent array elements. – ain Aug 11 '18 at 06:00
  • Possible duplicate of [Can I concurrently write different slice elements](https://stackoverflow.com/questions/49879322/can-i-concurrently-write-different-slice-elements/49879469#49879469). – icza Aug 11 '18 at 06:16
  • Besides the question, which already has been answered: Consider using a waitgroup for waiting for all init coroutines to complete instead of the channels. – Matthias247 Aug 11 '18 at 07:22

1 Answers1

2

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

edkeveked
  • 17,989
  • 10
  • 55
  • 93
  • Thank you very much! glad the issue is not with the synchronization part but with the closure property. – Ruomu Aug 11 '18 at 05:08