I noticed that if I tried appending to a slice using goroutines inside a for
loop, there would be instances where I would get missing/blank data:
destSlice := make([]myClass, 0)
var wg sync.WaitGroup
for _, myObject := range sourceSlice {
wg.Add(1)
go func(closureMyObject myClass) {
defer wg.Done()
var tmpObj myClass
tmpObj.AttributeName = closureMyObject.AttributeName
destSlice = append(destSlice, tmpObj)
}(myObject)
}
wg.Wait()
Sometimes, when I print all AttributeName
s from destSlice
, some elements are empty strings (""
), and other times, some elements from sourceSlice
are not present in destSlice
.
Does my code have a data race, and does this mean that append
is not thread-safe for concurrent use by multiple goroutines?
In Go no value is safe for concurrent read/write, slices (which are slice headers) are no exception.
Yes, your code has data races. Run with the
-race
option to verify.Example:
Running it with
Output is:
Solution is simple, use a
sync.Mutex
to protect writing thedestSlice
value:You could also solve it in other ways, e.g. you could use a channel on which you'd send the value to be appended, and have a designated goroutine receiving from this channel and do the append.
It's quite an old question but there is another small improvement that helps to get rid of mutex. You can use index to add to array. Each go routine will use it's own index. In this case synchronization is not necessary.
To give a more recent solution to this problem, it looks like Go has released a new map for sync purposes:
https://godoc.org/golang.org/x/sync/syncmap