【问题标题】:Thread-safe (Goroutine-safe) cache in GoGo 中的线程安全(Goroutine 安全)缓存
【发布时间】:2012-07-11 01:12:59
【问题描述】:

问题 1

我正在为我的服务器构建/搜索 RAM 内存缓存层。它是一个简单的 LRU 缓存,需要处理并发请求(均 Gets an Sets)。

我发现 https://github.com/pmylund/go-cache 声称是线程安全的。

就获取存储的接口而言,这是正确的。但是如果多个 goroutine 请求相同的数据,它们都在检索指向同一内存块的指针(存储在接口中)。如果任何 goroutine 更改了数据,这将不再是非常安全的。

有没有可以解决这个问题的缓存包?


问题 1.1

如果问题 1 的答案是 ,那么建议的解决方案是什么?
我看到两个选项:

替代方案 1
解决方案: 将值存储在带有 sync.Mutex 的包装结构中,这样每个 goroutine 都需要在读取/写入数据之前锁定数据
type cacheElement struct { value interface{}, lock sync.Mutex }
缺点: 缓存不知道对数据所做的更改,甚至可能已将其从缓存中删除。一个 goroutine 也可能锁定其他 goroutine。

备选方案 2
解决方案:制作数据副本(假设数据本身不包含指针)
缺点: 每次执行缓存 Get 时分配内存,更多垃圾回收。


对不起,多部分问题。但您不必全部回答。如果您对问题 1 有一个好的答案,那对我来说就足够了!

【问题讨论】:

  • 在缓存中存储指针会丢失缓存点。在备选方案 1 中;如果要使用互斥锁,则互斥锁应位于商店级别,而不是入门级别。

标签: caching interface thread-safety go lru


【解决方案1】:

备选方案 2 对我来说听起来不错,但请注意,您不必为每个 cache.Get() 复制数据。只要您的数据被认为是不可变的,您就可以同时通过多个读取器访问它。

如果您打算修改它,您只需创建一个副本。这个成语称为 COW(写时复制),在并发软件设计中很常见。它特别适合高读写比的场景(就像缓存一样)。

所以,每当你想修改一个缓存条目时,你基本上必须:

  1. 创建旧缓存数据的副本(如果有)。
  2. 修改数据(在这一步之后,数据应该被认为是不可变的,不得再更改)
  3. 添加/替换缓存中的现有元素。为此,您可以使用您之前指出的 go-cache 库(它基于锁),或者编写您自己的无锁库来简单地以原子方式交换指向数据元素的指针。

此时任何执行cache.Get操作的goroutine都会得到新的数据。然而,现有的 goroutines 可能仍在读取旧数据。因此,您的程序可能会同时对同一数据的许多不同版本进行操作。不过不用担心,只要所有 goroutine 访问完旧数据,GC 就会自动收集。

【讨论】:

  • 我可以让 goroutines 处理过时的副本没有问题,所以你的这个建议消除了备选方案 2 的垃圾收集开销问题的主要部分。我将开始实施这个想法。顺便说一句,编写“无锁”库是什么意思?您的意思是使用备选方案 2 而不是 1?
  • 不,我认为您绝对应该使用替代#2。忘记关于原子交换指针的部分,go-cache 可能对你有用。 (问题是 gocache 是基于互斥锁的,所以只有一个 goroutine 可能同时写入缓存,如果一个 goroutine 在获取锁后被重新调度,系统可能在相当长的时间内根本没有任何进展. 原子交换指针可以避免这个问题,但你应该对并发编程有很好的了解。)
  • 请注意,go-routines 当前不可抢占,因此 go-routines 不能被重新调度,除非它调用一些导致 (go) 调度程序启动的运行时 API。
  • @tux21b:由于我没有这种低级编程的经验,使用原子比较和交换,我将从基于互斥体的缓存和普通的map-lookup开始,使用COW设计. Rob Pike 本人曾说过 (lysator.liu.se/c/pikestyle.html) 最好在微调速度之前进行测量,所以我现在就从简单的开始。
【解决方案2】:

tux21b 给出了一个很好的答案。我只是指出您不必返回指向数据的指针。您可以将非指针值存储在缓存中,并且 go 将按值传递,这将是一个副本。那么您的 Get 和 Set 方法将是安全的,因为实际上没有任何东西可以修改缓存内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-15
    • 2011-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-25
    相关资源
    最近更新 更多