在 Golang 中,原生的 map
类型并不支持并发安全,也没有内置的键过期机制。不过,有一些社区提供的库和方案可以满足这两个需求:线程安全和键过期。
1. 使用 sync.Map
(线程安全,但不支持过期)
Golang 提供了线程安全的 sync.Map
,但它没有键过期功能。如果只需要线程安全,可以直接使用:
import ("fmt""sync"
)func main() {var m sync.Mapm.Store("key1", "value1") // 写入键值val, ok := m.Load("key1") // 读取键值if ok {fmt.Println("key1:", val)}m.Delete("key1") // 删除键值
}
限制:
sync.Map
适用于高并发场景,但需要自行实现键的过期功能。
2. 使用开源库 go-cache
(推荐:支持线程安全和键过期)
go-cache 是一个轻量级、高效的内存缓存库,支持线程安全和键过期功能。
安装
go get github.com/patrickmn/go-cache
使用示例
package mainimport ("fmt""time""github.com/patrickmn/go-cache"
)func main() {// 创建一个缓存对象,默认过期时间为 5 分钟,清理间隔为 10 分钟c := cache.New(5*time.Minute, 10*time.Minute)// 设置键值,并指定过期时间c.Set("key1", "value1", cache.DefaultExpiration) // 默认过期时间c.Set("key2", "value2", 10*time.Second) // 自定义过期时间// 读取键值val, found := c.Get("key1")if found {fmt.Println("key1:", val)} else {fmt.Println("key1 has expired or not found")}// 检查键是否存在_, exists := c.Get("key2")fmt.Println("key2 exists:", exists)// 删除键c.Delete("key2")
}
特点
- 线程安全。
- 支持键过期,自动清理。
- 提供多种方法(如读取、删除、批量操作等)。
3. 使用 expiremap
(支持自动过期和并发安全)
expiremap
是另一个简洁的库,专门为自动过期的键值存储设计。
安装
go get github.com/zyedidia/expiremap
使用示例
package mainimport ("fmt""time""github.com/zyedidia/expiremap"
)func main() {// 创建一个过期 map,键值过期时间为 2 秒m := expiremap.New(time.Second * 2)// 设置键值m.Set("key1", "value1")m.Set("key2", "value2")// 读取键值val, ok := m.Get("key1")if ok {fmt.Println("key1:", val)} else {fmt.Println("key1 has expired or does not exist")}// 等待 3 秒后,键值会自动过期time.Sleep(3 * time.Second)_, ok = m.Get("key1")fmt.Println("key1 exists after 3 seconds:", ok)
}
特点
- 键过期时间由
time.Duration
控制。 - 自动清理过期键。
- 支持线程安全。
4. 自己实现一个安全且支持过期的 map
如果你不想使用外部库,可以结合 sync.RWMutex
和 time.Timer
自行实现:
示例代码
package mainimport ("fmt""sync""time"
)type SafeMap struct {data map[string]anymutex sync.RWMutex
}func NewSafeMap() *SafeMap {return &SafeMap{data: make(map[string]any),}
}func (sm *SafeMap) Set(key string, value any, duration time.Duration) {sm.mutex.Lock()defer sm.mutex.Unlock()sm.data[key] = value// 启动一个定时器删除键go func() {time.Sleep(duration)sm.mutex.Lock()delete(sm.data, key)sm.mutex.Unlock()}()
}func (sm *SafeMap) Get(key string) (any, bool) {sm.mutex.RLock()defer sm.mutex.RUnlock()val, ok := sm.data[key]return val, ok
}func (sm *SafeMap) Delete(key string) {sm.mutex.Lock()defer sm.mutex.Unlock()delete(sm.data, key)
}func main() {sm := NewSafeMap()sm.Set("key1", "value1", 5*time.Second) // 设置 5 秒过期val, ok := sm.Get("key1")fmt.Println("key1 exists:", ok, "value:", val)// 等待 6 秒,确保键已过期time.Sleep(6 * time.Second)val, ok = sm.Get("key1")fmt.Println("key1 exists after expiration:", ok)
}
特点
sync.RWMutex
确保并发安全。- 使用
time.Timer
实现键过期。
总结
- 如果需要简单易用的解决方案,推荐使用
go-cache
。 - 如果你需要更轻量的库,
expiremap
是一个好选择。 - 对于特定需求,可以自行实现线程安全的
map
,结合定时器实现过期功能。