Home >Backend Development >Golang >Why is appending to slices in Go thread-unsafe?

Why is appending to slices in Go thread-unsafe?

Susan Sarandon
Susan SarandonOriginal
2024-11-09 17:30:02283browse

Why is appending to slices in Go thread-unsafe?

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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn