Sobes.tech
Middle+

How safe is it to pass slices to different goroutines?

sobes.tech AI

Answer from AI

Passing slices directly to different goroutines is unsafe if goroutines modify them simultaneously without synchronization or if the slice is modified in one goroutine after passing (by copying the header) to another.

Reasons for unsafety:

  • Shared underlying array: Slices are headers (pointer to the underlying array, length, capacity) pointing to a common array.
  • Race condition: Concurrently writing to the same element of the underlying array or changing its size from different goroutines will lead to race conditions, unpredictable results, and errors.

Safe ways to work with slices in goroutines:

  1. Passing a copy of the slice: Create a full copy of the slice and pass it. This is safe because goroutines work with separate data.

    // creating a full copy of the slice
    newSlice := make([]int, len(originalSlice))
    copy(newSlice, originalSlice)
    
  2. Using channels: Pass slice elements or the slice itself through channels. Channels provide synchronization and data isolation.

    // passing elements through a channel
    ch := make(chan int)
    go func() {
        for _, v := range originalSlice {
            ch <- v
        }
        close(ch)
    }()
    
    // passing the slice through a channel
    chSlice := make(chan []int)
    go func() {
        chSlice <- originalSlice // passing the header by value, but channel synchronizes
                                // important: do not modify originalSlice after sending
    }()
    
  3. Synchronization with sync.Mutex or sync.RWMutex: Protect access to the slice using mutexes. This ensures only one goroutine can modify the slice at any given time.

    import "sync"
    
    var mu sync.Mutex
    var sharedSlice []int
    
    func modifySlice(index, value int) {
        mu.Lock()
        defer mu.Unlock()
        if index < len(sharedSlice) {
            sharedSlice[index] = value
        }
    }
    

Comparison of approaches:

Approach Safety Performance with large slices Complexity
Slice copy High Lower (data copying) Simple
Channels High Depends (channel operation cost) Medium
Mutexes High High (if no severe conflicts) Medium

Thus, passing the slice header by default to different goroutines without additional synchronization or copying is unsafe. It is necessary to use one of the above methods to ensure data safety.