当前位置: 首页 > 工具软件 > GroupCache > 使用案例 >

groupcache源码(三)-cache对lru的进一步封装

杨骏
2023-12-01

一.简介

lur.Cache是对数据lru的实现,但并不是并发安全的。groupcache.cache进行了进一步的并发封装

二. 基本数据结构

// cache is a wrapper around an *lru.Cache that adds synchronization,
// makes values always be ByteView, and counts the size of all keys and
// values.
type cache struct {
	mu         sync.RWMutex
	nbytes     int64 // of all keys and values
	lru        *lru.Cache
	nhit, nget int64
	nevict     int64 // number of evictions
}

并发控制主要通过 sync.RWMutex实现,并且数据的值总是 ByteView

三. 数据添加

func (c *cache) add(key string, value ByteView) {
// 加锁
	c.mu.Lock()
// 利用defer 进行最后的释放锁
	defer c.mu.Unlock()
	if c.lru == nil {
// 如果lru为nil,则进行初始化
		c.lru = &lru.Cache{
// 数据被删除时的钩子函数
			OnEvicted: func(key lru.Key, value interface{}) {
				val := value.(ByteView)
// 减去字节数
				c.nbytes -= int64(len(key.(string))) + int64(val.Len())
// 删除的数据量 ++
				c.nevict++
			},
		}
	}
// 数据添加
	c.lru.Add(key, value)
// 记录key和value总字节数
	c.nbytes += int64(len(key)) + int64(value.Len())
}

通过代码可以发现:

1.并发控制就是通过对lru操作时加锁实现的。

2. 释放锁使用defer,可以保证即使逻辑特别复杂,也能在方法执行完后,可以释放锁,避免某些逻辑下 忘记锁的释放

3. 通过nbytes和nevict字段进行额外信息的记录

四. 数据获取

func (c *cache) get(key string) (value ByteView, ok bool) {
// 加锁和释放锁
	c.mu.Lock()
	defer c.mu.Unlock()
// 数据获取次数 +1
	c.nget++
// lru未初始化,直接结束
	if c.lru == nil {
		return
	}
// 数据获取
	vi, ok := c.lru.Get(key)
	if !ok {
		return
	}
// 命中次数 +1
	c.nhit++
// 返回数据, value存储的是ByteView
	return vi.(ByteView), true
}

数据获取逻辑也比较简单,加锁->数据获取->额外信息记录->释放锁->数据返回

五. 其他方法

func (c *cache) items() int64 {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.itemsLocked()
}

其他方法逻辑类似,加锁,然后操作lru来实现并发安全

六. 总结

1. 使用sync.RWMutex进行并发安全控制

2. 使用defer进行锁释放,保障锁能够被释放

3. 结构体字段设计时,考虑钩子函数,满足外部的定制开发

 类似资料: