Go语言中的map不支持直接设置键的超时时间,因此我们可以通过以下方法实现:
维护一个存储key超时时间的map,以及一个实际存储数据的map。
在插入数据时,同时将key和对应的超时时间存入第一个map中。
启动一个goroutine来定期遍历第一个map,删除超时的key,并在第二个map中也进行删除操作。
在查询数据时,先判断该key是否过期。如果已经过期,则从第二个map中删除并返回空值;否则返回正常的值。
下面是示例代码:
package main
import (
"fmt"
"sync"
"time"
)
type TimeoutMap struct {
m map[string]interface{} // 存储实际数据
expMap map[string]int64 // 存储key的超时时间
mutex sync.RWMutex // 读写锁,保证并发安全
}
func NewTimeoutMap() *TimeoutMap {
tm := &TimeoutMap{
m: make(map[string]interface{}),
expMap: make(map[string]int64),
mutex: sync.RWMutex{},
}
go tm.cleanUp() // 启动清理过期键值对的协程
return tm
}
// 设置键值对,并指定过期时间(秒)
func (tm *TimeoutMap) Set(key string, value interface{}, timeout int64) {
tm.mutex.Lock()
defer tm.mutex.Unlock()
expireTime := time.Now().Unix() + timeout
tm.m[key] = value
tm.expMap[key] = expireTime
}
// 获取键值对,并判断是否过期。如果过期则删除并返回空值。
func (tm *TimeoutMap) Get(key string) interface{} {
tm.mutex.RLock()
defer tm.mutex.RUnlock()
if val, ok := tm.m[key]; ok {
if expireTime, ok := tm.expMap[key]; !ok || expireTime < time.Now().Unix() {
delete(tm.m, key)
delete(tm.expMap, key)
return nil
} else {
return val
}
} else {
return nil
}
}
// 清理过期键值对的协程
func (tm *TimeoutMap) cleanUp() {
for {
<-time.After(1 * time.Second) // 每秒检查一次
now := time.Now().Unix()
keysToDelete := []string{}
tm.mutex.Lock()
for key, expTime := range tm.expMap {
if expTime < now { // 如果已经过期,则记录需要删除的key
keysToDelete = append(keysToDelete, key)
}
}
// 删除需要删除的key
for _, key := range keysToDelete {
delete(tm.m, key)
delete(tm.expMap, key)
}
tm.mutex.Unlock()
}
}
func main() {
m := NewTimeoutMap()
m.Set("key1", "value1", 3) // 设置超时时间为3秒
fmt.Println(m.Get("key1")) // 输出"value1"
time.Sleep(4 * time.Second)
fmt.Println(m.Get("key1")) // 输出"<nil>"
}