Golang实践:用Sync.Map实现简易内存缓存系统

本文介绍了如何使用Go语言创建一个简单的Cache结构,利用sync.Map作为底层数据结构,实现Set和Get方法,带有过期时间管理。示例展示了如何设置和获取缓存项,以及过期后的处理。
摘要由CSDN通过智能技术生成

介绍

定义了一个Cache结构体,其中使用sync.Map作为底层数据结构来存储缓存项。Set方法用于设置缓存项,指定键、值以及过期时间。Get方法用于获取缓存项,如果缓存项存在且未过期,则返回值和true,否则返回nil和false。方法的接受者为指针类型,是为了对Cache对象进行操作,并在方法内部访问和修改对象的数据。
在main函数中,创建了一个Cache实例,并设置了两个缓存项。然后,我们尝试获取一个存在的缓存项,并在控制台上打印值。接着,我们等待6秒,再次尝试获取同一个缓存项,这次应该返回Key not found,因为该缓存项已过期。

代码

GoPratice/memcache/v0/simple.go at main · honlu/GoPratice

package main

import (
	"fmt"
	"sync"
	"time"
)

type Cache struct {
	data sync.Map
}

type cacheItem struct {
	value   interface{} // 值
	expired time.Time   // 过期时间
}

// Set 指针类型的方法接受者,才可以保证在方法内部访问和修改对象的数据。
func (c *Cache) Set(key string, value interface{}, duration time.Duration) {
	c.data.Store(key, cacheItem{
		value:   value,
		expired: time.Now().Add(duration),
	})
}

func (c *Cache) Get(key string) (interface{}, bool) {
	item, ok := c.data.Load(key)
	if !ok {
		return nil, false
	}

	cacheItem := item.(cacheItem)
	if time.Now().After(cacheItem.expired) { // 判断是否过期
		c.data.Delete(key)
		return nil, false
	}

	return cacheItem.value, true
}

func main() {
	cache := &Cache{}

	cache.Set("key1", "value1", 5*time.Second)
	cache.Set("key2", "value2", 10*time.Second)

	value1, found1 := cache.Get("key1")
	if found1 {
		fmt.Printf("Value1:%v\n", value1)
	} else {
		fmt.Printf("Key1 not found\n")
	}

	value2, found2 := cache.Get("key2")
	if found2 {
		fmt.Printf("Value2:%v\n", value2)
	} else {
		fmt.Printf("Key2 not found\n")
	}

	time.Sleep(6 * time.Second)

	value1, found1 = cache.Get("key1")
	if found1 {
		fmt.Printf("Value1:%v\n", value1)
	} else {
		fmt.Printf("Key1 not found\n")
	}
	value2, found2 = cache.Get("key2")
	if found2 {
		fmt.Printf("Value2:%v\n", value2)
	} else {
		fmt.Printf("Key2 not found\n")
	}
}

后续

可以考虑具体需求进行扩展和优化,例如添加缓存淘汰策略、高并发等。

备注:

内容首发在个人知乎文章,源码在Github

sync.Map 是 Go 语言标准库中提供的一种并发安全的字典类型,它可以被多个 goroutine 安全地访问和修改。在多个 goroutine 中并发地读写一个 map 时,会出现竞争条件,从而导致数据不一致。而 sync.Map 利用了一些锁的技巧,避免了这种竞争条件的发生,从而实现了高效的并发安全访问。 sync.Map 的 API 非常简单,主要包括以下几个方法: 1. Store(key, value interface{}):将一个键值对存储到 sync.Map 中。 2. Load(key interface{}) (value interface{}, ok bool):根据键从 sync.Map 中获取对应的值。 3. LoadOrStore(key, value interface{}) (actual interface{}, loaded bool):如果键存在于 sync.Map 中,则返回对应的值和 true,否则将键值对存储到 sync.Map 中并返回新的值和 false。 4. Delete(key interface{}):从 sync.Map 中删除一个键值对。 5. Range(f func(key, value interface{}) bool):遍历 sync.Map 中的键值对,并对每一个键值对调用函数 f,如果 f 返回 false,则停止遍历。 下面是一个使用 sync.Map 的简单例子,展示了如何在多个 goroutine 中并发地访问和修改 sync.Map: ``` package main import ( "fmt" "sync" ) func main() { var m sync.Map var wg sync.WaitGroup wg.Add(2) // goroutine 1: 向 sync.Map 中存储键值对 go func() { defer wg.Done() m.Store("key1", "value1") m.Store("key2", "value2") }() // goroutine 2: 从 sync.Map 中加载键值对 go func() { defer wg.Done() if v, ok := m.Load("key1"); ok { fmt.Println("value for key1:", v) } if v, ok := m.Load("key2"); ok { fmt.Println("value for key2:", v) } }() wg.Wait() } ``` 在上面的例子中,我们首先创建了一个 sync.Map 对象 m。然后在两个 goroutine 中同时访问这个对象,一个 goroutine 向其中存储键值对,另一个 goroutine 则从其中加载键值对。由于 sync.Map 是并发安全的,所以这两个 goroutine 可以并发地访问和修改 sync.Map,而不会出现竞争条件。 需要注意的是,虽然 sync.Map 是并发安全的,但它并不是用来替代普通的 map 的。如果你只是需要在某个 goroutine 中访问和修改一个 map,那么你应该使用普通的 map,因为 sync.Map 的性能会比较差。只有在需要多个 goroutine 并发地访问和修改一个 map 时,才应该考虑使用 sync.Map
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值