go
var m sync.Mutex
//sync.Mutex 是一个互斥锁,可以由不同的协程加锁和解锁。
//sync.Mutex 是 Go 语言标准库提供的一个互斥锁
//当一个协程(goroutine)获得了这个锁的拥有权后,其它请求锁的协程(goroutine)就会阻塞在 Lock() 方法的调用上,直到调用 Unlock() 锁被释放。
var set = make(map[int]bool, 0)
func printOnce(num int) {
m.Lock()
defer m.Unlock()
if _, exist := set[num]; !exist {
fmt.Println(num)
}
set[num] = true
}
func main() {
for i := 0; i < 10; i++ {
go printOnce(100)
}
time.Sleep(time.Second)
}
- 接下来考虑用
sync.Mutex
封装LRU
缓存淘汰策略的几个方法,使之支持并发的读写。 - 抽象了一个 只读 数据结构
ByteView
用来表示缓存值,是GeeCache
主要的数据结构之一。 ByteView
是对实际缓存的⼀层封装,因为实际的缓存值是⼀个byte
切⽚存储的,⽽切⽚的底层是⼀个指向底层数组的指针,⼀个记录⻓度的变量和⼀个记录容量的变量。- 为什么要用
byte
切片?因为byte
支持任意数据类型的存储。byte
是uint8
的别名:在 Go 中,byte
实际上是uint8
的一种别名,它表示0
到255
范围内的无符号整数。- 在计算机中,所有数据(无论是文本、图片、视频还是其他格式)最终都会以二进制形式存储和处理。二进制数据可以被视为一系列字节(
[]byte
),而byte
则是这种数据的基本单位。
go
b := []byte{'a', 'b', 'c'}
// b的底层数据结构如下:
// 一个指针指向底层数组 [a, b, c]
// len(b) = 3
// cap(b) b的容量,取决于底层数组的大小
- 如果获取缓存值时直接返回缓存值的切⽚,那个切⽚只是原切⽚三个变量的拷⻉,真正的缓存值就可能被外部恶意修改。
- 这里可能一开始会有点难理解:为什么直接返回切片有问题?
- 切片返回的是一个浅拷贝:切片的复制只会复制切片的三个元数据(指针、长度、容量),而不会复制底层数组;新切片和原切片共用同一个底层数组。
- 缓存值可能被修改:如果外部通过返回的切片修改数据,实际上会直接修改底层数组的内容。
go
func main() {
cache := []byte{'x', 'y', 'z'}
external := cache // 浅拷贝,仅复制指针、长度和容量
external[0] = 'a' // 修改 external 也会影响 cache
fmt.Println(string(cache)) // 输出 "ayz"
}
- 所以⽤
ByteView
进⾏⼀层封装,返回缓存值时的ByteView
则是⼀个原切⽚的深拷⻉。 - 如何通过
ByteView
解决问题?ByteView
的设计目的是通过深拷贝避免直接暴露底层数组。- 封装数据:
ByteView
持有一个不可直接访问的字段b
,存储了原始缓存数据的拷贝。外部无法直接获取和修改底层数据。 - 深拷贝的实现:通过
cloneBytes
函数,对底层数据进行深拷贝------新建一个独立的字节数组,将原始数据逐字节复制到新数组中。 - 只读访问:通过
ByteSlice
和String
等方法,外部只能访问数据的副本或只读视图,无法影响原始数据。
go
package geecache
// A ByteView holds an immutable view of bytes.
type ByteView struct {
b []byte
}
// Len returns the view's length
func (v ByteView) Len() int {
return len(v.b)
}
// ByteSlice returns a copy of the data as a byte slice.
func (v ByteView) ByteSlice() []byte {
return cloneBytes(v.b)
}
// String returns the data as a string, making a copy if necessary.
func (v ByteView) String() string {
return string(v.b)
}
func cloneBytes(b []byte) []byte {
c := make([]byte, len(b))
copy(c, b)
return c
}
- 接下来为 lru.Cache 添加并发特性。