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:
-
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) -
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 }() -
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.