可能引发的问题
- 并发安全问题:多个协程同时对切片进行读写操作时,可能导致数据不一致。例如,一个协程正在读取切片中的某个元素,而另一个协程同时对切片进行扩容并修改了该元素,这就会导致读取到的数据不正确。
- 资源竞争问题:在高并发环境下,多个协程可能同时尝试对切片进行扩容。由于切片的扩容涉及内存的重新分配和数据的复制,这可能导致资源竞争,例如多个协程同时申请内存,可能造成内存分配混乱,进而影响程序性能和稳定性。
设计层面优化
- 使用sync包:可以利用
sync.Mutex
或sync.RWMutex
来保护对切片的操作。对于读多写少的场景,sync.RWMutex
更合适,它允许多个读操作同时进行,但写操作时会独占切片,防止其他读写操作。
- 数据分片:将数据按照一定规则分片存储在多个切片中,不同协程操作不同的切片,减少竞争。例如,按照哈希值将数据分配到不同切片,每个协程负责处理特定哈希值范围内的数据。
代码实现层面优化
- 使用
sync.Mutex
示例
package main
import (
"fmt"
"sync"
)
var (
slice []int
mu sync.Mutex
)
func addToSlice(num int) {
mu.Lock()
slice = append(slice, num)
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
addToSlice(n)
}(i)
}
wg.Wait()
fmt.Println(slice)
}
- 使用
sync.RWMutex
示例
package main
import (
"fmt"
"sync"
)
var (
slice []int
rwmu sync.RWMutex
)
func readSlice() {
rwmu.RLock()
fmt.Println(slice)
rwmu.RUnlock()
}
func writeSlice(num int) {
rwmu.Lock()
slice = append(slice, num)
rwmu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
writeSlice(n)
}(i)
}
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
defer wg.Done()
readSlice()
}()
}
wg.Wait()
}
- 数据分片示例
package main
import (
"fmt"
"sync"
)
const numShards = 10
var (
shards [numShards][]int
mu [numShards]sync.Mutex
)
func addToShard(num int) {
shardIndex := num % numShards
mu[shardIndex].Lock()
shards[shardIndex] = append(shards[shardIndex], num)
mu[shardIndex].Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
addToShard(n)
}(i)
}
wg.Wait()
for i := 0; i < numShards; i++ {
fmt.Printf("Shard %d: %v\n", i, shards[i])
}
}