在 Go 的地图中实现原子计数器的最佳方式

问题描述 投票:0回答:3

我想在地图中实现一个原子计数器(

map[string]int64
),它如何在Go中实现?首先,我不能只使用 int64 作为键,因为我不能在 map 中为
atomic.AddInt64
func 取这个值的地址,因为 map 是不可寻址的。其次,当使用 *int64 作为值时,我必须以某种方式为指针预初始化 int 值,但是
if _, ok := myMap[key]; !ok { myMap[key] = ... }
上的初始化将不起作用,因为它不是原子的,因此需要锁。

go concurrency atomic
3个回答
2
投票

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
    确保通过自动查找地图,如果不存在则放置条目,否则从地图获取条目。

  • 第二步是在获得规范条目后增加计数器。由于对同一键的所有查找都返回相同的条目,因此这一步很简单。


0
投票

Golang 在 sync 包中有一个同步映射,如 here 所述,它说:

当多个 goroutine 读取、写入和覆盖不相交的键集的条目时。

这似乎是你的用例,它说它比带有互斥锁的地图更好。


0
投票

我认为 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()

© www.soinside.com 2019 - 2024. All rights reserved.