ANi*_*sus 3 caching interface thread-safety go lru
问题1
我正在为服务器构建/搜索RAM内存缓存层。它是一个简单的LRU缓存,需要处理并发请求(均获取一个Set)。
我发现https://github.com/pmylund/go-cache声称是线程安全的。
就获取存储的接口而言,这是正确的。但是,如果多个goroutines请求相同的数据,它们都将检索指向同一块内存的指针(存储在接口中)。如果有任何goroutine更改了数据,那么这将不再是非常安全的。
是否有任何可以解决此问题的缓存程序包?
问题1.1
如果对问题1的回答为否,那么建议的解决方案是什么?
我看到两个选择:
备选方案1
解决方案:使用值将值存储在包装结构中,sync.Mutex以便每个goroutine在读取/写入数据之前都需要锁定数据。
type cacheElement struct { value interface{}, lock sync.Mutex }
缺点:缓存不知道对数据所做的更改,甚至可能已将其从缓存中删除。一个goroutine可能还会锁定其他goroutine。
备选方案2
解决方案:制作数据的副本(假设数据本身不包含指针)
缺点:每次执行缓存Get时的内存分配都会进行更多的垃圾回收。
对不起,这个问题很复杂。但是您不必全部回答。如果您对问题1有一个好的答案,那对我来说就足够了!
替代方法2对我来说听起来不错,但请注意,您不必复制每个方法的数据cache.Get()。只要可以将您的数据视为不可变的,就可以同时使用多个读取器来访问它们。
如果您要修改副本,则只需创建一个副本。这种习惯用法称为COW(写时复制),在并发软件设计中非常常见。它特别适合具有高读/写比的场景(就像高速缓存一样)。
因此,无论何时要修改缓存的条目,您基本上都必须:
此时,任何执行cache.Get操作的goroutine 都会获取新数据。但是,现有的goroutine可能仍在读取旧数据。因此,您的程序可能会同时在同一数据的许多不同版本上运行。但是请放心,所有goroutine一旦完成对旧数据的访问,GC就会自动收集它。