1. 选择合适的同步机制
- 读写锁(
sync.RWMutex
):
- 设计思路:适用于读多写少的场景。读操作使用读锁,允许多个协程同时读,写操作使用写锁,写锁会阻塞其他读和写操作。在Go语言中,
sync.RWMutex
提供了 RLock
和 RUnlock
用于读操作,Lock
和 Unlock
用于写操作。例如:
package main
import (
"fmt"
"sync"
)
var (
dataMap = make(map[string]int)
mu sync.RWMutex
)
func read(key string) int {
mu.RLock()
defer mu.RUnlock()
return dataMap[key]
}
func write(key string, value int) {
mu.Lock()
defer mu.Unlock()
dataMap[key] = value
}
- **预期效果**:在保证数据一致性的前提下,大大提高读操作的并发性能,减少读操作的等待时间。
- 互斥锁(
sync.Mutex
):
- 设计思路:简单直接,适用于读写操作频率较为均衡或写操作较多的场景。任何对map的读写操作都先获取互斥锁,操作完成后释放锁。例如:
package main
import (
"fmt"
"sync"
)
var (
dataMap = make(map[string]int)
mu sync.Mutex
)
func access(key string, value int, write bool) {
mu.Lock()
defer mu.Unlock()
if write {
dataMap[key] = value
} else {
fmt.Println(dataMap[key])
}
}
- **预期效果**:保证数据的一致性,但由于每次读写都需要获取锁,在高并发场景下可能会成为性能瓶颈。
2. 数据结构调整
- 分段锁(Sharded Map):
- 设计思路:将大的map分成多个小的map,每个小map对应一个独立的锁。例如,假设有10个小map,根据key的哈希值取模决定操作哪个小map及其对应的锁。这样,不同的协程可以同时操作不同的小map,减少锁的竞争。
package main
import (
"fmt"
"sync"
)
const numShards = 10
type ShardedMap struct {
shards []shard
}
type shard struct {
mu sync.Mutex
data map[string]int
}
func NewShardedMap() *ShardedMap {
sm := &ShardedMap{
shards: make([]shard, numShards),
}
for i := range sm.shards {
sm.shards[i].data = make(map[string]int)
}
return sm
}
func (sm *ShardedMap) getShard(key string) *shard {
hash := int(hash(key))
return &sm.shards[hash%numShards]
}
func (sm *ShardedMap) Get(key string) int {
sh := sm.getShard(key)
sh.mu.Lock()
defer sh.mu.Unlock()
return sh.data[key]
}
func (sm *ShardedMap) Set(key string, value int) {
sh := sm.getShard(key)
sh.mu.Lock()
defer sh.mu.Unlock()
sh.data[key] = value
}
func hash(s string) uint32 {
// 简单的哈希函数示例
var h uint32
for _, c := range s {
h = 31*h + uint32(c)
}
return h
}
- **预期效果**:显著减少锁的竞争,提高并发性能,尤其是在高并发场景下。
- sync.Map:
- 设计思路:Go 1.9 引入的
sync.Map
是一个线程安全的map,它内部采用了一种基于读写分离的设计。它会维护两个map,一个是只读的map(read),一个是读写的map(dirty)。读操作优先从read map中读取,写操作则先更新dirty map,当dirty map的元素数量达到一定阈值时,会将dirty map提升为read map。
package main
import (
"fmt"
"sync"
)
var data sync.Map
func set(key string, value int) {
data.Store(key, value)
}
func get(key string) (int, bool) {
val, ok := data.Load(key)
if!ok {
return 0, false
}
return val.(int), true
}
- **预期效果**:提供了一种高效的线程安全map实现,适用于高并发读写场景,减少锁的争用,性能较好。但它不支持遍历操作,且内存使用相对较高。