在Golang中实现计数器组(多个计数器)有多种方案,以下是几种常见实现方式:
1. 使用sync.Map(适用于高并发场景)
package main
import (
"sync"
"sync/atomic"
)
type CounterGroup struct {
counters sync.Map
}
func (cg *CounterGroup) Increment(key string) {
for {
if actual, loaded := cg.counters.LoadOrStore(key, int64(1)); loaded {
if atomic.AddInt64(actual.(*int64), 1) == 1 {
// 防止计数器被重置后出现竞争
cg.counters.Store(key, actual)
}
break
} else {
cg.counters.Store(key, &[]int64{1}[0])
break
}
}
}
func (cg *CounterGroup) Get(key string) int64 {
if val, ok := cg.counters.Load(key); ok {
return atomic.LoadInt64(val.(*int64))
}
return 0
}
2. 使用map + RWMutex(适用于中等并发)
type CounterGroupMutex struct {
mu sync.RWMutex
counters map[string]*int64
}
func NewCounterGroupMutex() *CounterGroupMutex {
return &CounterGroupMutex{
counters: make(map[string]*int64),
}
}
func (cg *CounterGroupMutex) Increment(key string) {
cg.mu.Lock()
defer cg.mu.Unlock()
if counter, exists := cg.counters[key]; exists {
atomic.AddInt64(counter, 1)
} else {
newCounter := int64(1)
cg.counters[key] = &newCounter
}
}
func (cg *CounterGroupMutex) Get(key string) int64 {
cg.mu.RLock()
defer cg.mu.RUnlock()
if counter, exists := cg.counters[key]; exists {
return atomic.LoadInt64(counter)
}
return 0
}
3. 使用分片减少锁竞争
type ShardedCounterGroup struct {
shards []*CounterShard
}
type CounterShard struct {
mu sync.RWMutex
counters map[string]int64
}
func NewShardedCounterGroup(shardCount int) *ShardedCounterGroup {
shards := make([]*CounterShard, shardCount)
for i := range shards {
shards[i] = &CounterShard{
counters: make(map[string]int64),
}
}
return &ShardedCounterGroup{shards: shards}
}
func (scg *ShardedCounterGroup) getShard(key string) *CounterShard {
hash := fnv.New32a()
hash.Write([]byte(key))
return scg.shards[hash.Sum32()%uint32(len(scg.shards))]
}
func (scg *ShardedCounterGroup) Increment(key string) {
shard := scg.getShard(key)
shard.mu.Lock()
defer shard.mu.Unlock()
shard.counters[key]++
}
性能考虑
- sync.Map方案:适合读多写少,键数量不确定的场景
- map+RWMutex:实现简单,适合键数量可控的场景
- 分片方案:适合高并发写入,通过分散锁竞争提升性能
选择哪种方案取决于具体的并发需求、计数器数量和性能要求。