Why Appending to Slices Can Be Thread-Unsafe
When multiple goroutines attempt to append data to a slice concurrently, a data race condition can occur. This is because slices in Go are not thread-safe, meaning multiple goroutines can access and modify the same slice header simultaneously, potentially causing data corruption.
Data Race Illustration
Consider the following code:
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()
In this code, multiple goroutines concurrently append to the destSlice slice. This can lead to missing or blank data in the resulting slice, as goroutines may interleave their operations and overwrite each other's changes.
Verifying Data Races with the "-race" Option
Running the code with the "-race" option will generate a warning for each data race detected. The following output illustrates the data race conditions in the provided code:
================== WARNING: DATA RACE Read at 0x00c420074000 by goroutine 6: main.main.func1() /home/icza/gows/src/play/play.go:20 +0x69 Previous write at 0x00c420074000 by goroutine 5: main.main.func1() /home/icza/gows/src/play/play.go:20 +0x106 Goroutine 6 (running) created at: main.main() /home/icza/gows/src/play/play.go:21 +0x1cb Goroutine 5 (running) created at: main.main() /home/icza/gows/src/play/play.go:21 +0x1cb ==================
Solution: Using a Mutex for Synchronization
To ensure thread-safe concurrent appends, you can use a mutex to protect the destSlice slice header. The following modified code demonstrates this:
var ( mu = &sync.Mutex{} 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 mu.Lock() destSlice = append(destSlice, tmpObj) mu.Unlock() }(myObject) } wg.Wait()
By acquiring the mutex before each append operation, you prevent multiple goroutines from simultaneously modifying the slice header, ensuring data integrity and eliminating the data race condition.
The above is the detailed content of Why is appending to slices in Go thread-unsafe?. For more information, please follow other related articles on the PHP Chinese website!