我想在地图中实现一个原子计数器(
map[string]int64
),它如何在Go中实现?首先,我不能只使用 int64 作为键,因为我不能在 map 中为 atomic.AddInt64
func 取这个值的地址,因为 map 是不可寻址的。其次,当使用 *int64 作为值时,我必须以某种方式为指针预初始化 int 值,但是 if _, ok := myMap[key]; !ok { myMap[key] = ... }
上的初始化将不起作用,因为它不是原子的,因此需要锁。
sync.Map
具有适用于您的用例的确切方法,即 (*sync.Map).LoadOrStore
,如果可以,它将加载现有值,否则将给定值存储到地图中。
func UpdateCounter(counters *sync.Map, key string) {
val, _ := counters.LoadOrStore(key, new(int64))
ptr := val.(*int64)
atomic.AddInt64(ptr, 1)
}
分两步完成:
第一个是规范化条目。即无论执行顺序如何,都为相同的密钥获得相同的条目。
(*sync.Map).LoadOrStore
确保通过自动查找地图,如果不存在则放置条目,否则从地图获取条目。
第二步是在获得规范条目后增加计数器。由于对同一键的所有查找都返回相同的条目,因此这一步很简单。
我认为 LoadOrStore 不会在这里提供帮助,因为您正在尝试为读写(增量)操作创建原子 transaction。 最好为您要更新的每个条目使用锁图:
urlCache = expiringmap.New[string, int](config.ConfigFile.Ttl)
enrtyLock, _ := r.urlCacheLocks.Get(url)
enrtyLock.Lock()
//your get and set transaction here//
enrtyLock.Unlock()