1 回答
TA贡献1810条经验 获得超4个赞
我认为您的测试有时会失败,因为您的缓存无法保证它只获取一次项目,而且您很幸运测试抓住了这一点。
如果一个 item 不在其中,并且 2 个并发 goroutines 同时调用Cache.GetItem(),可能lockMap.Load()会在两者中报告 key 不在 map 中,两个 goroutines 都创建一个sync.Once,并且都将自己的实例存储在 map 中(显然只有一个——后者——将保留在地图中,但您的缓存不会检查这一点)。
然后 2 个 goroutine 都会调用client.GetItem(),因为 2 个单独sync.Once的不提供同步。只有使用相同的sync.Once实例,才能保证传递给的函数Once.Do()只执行一次。
我认为sync.Mutex避免sync.Once在这里创建和使用 2 会更容易、更合适。
或者由于您已经在使用sync.Map,您可以使用以下Map.LoadOrStore()方法:创建一个sync.Once,并将其传递给Map.LoadOrStore(). 如果键已经在地图中,请使用返回的sync.Once. 如果密钥不在地图中,您sync.Once将存储在其中,因此您可以使用它。这将确保没有多个并发 goroutine 可以sync.once在其中存储多个实例。
像这样的东西:
var once *sync.Once
if entry, loaded := provider.lockMap.LoadOrStore(key, once); loaded {
// Was already in the map, use the loaded Once
once = entry.(*sync.Once)
}
这个解决方案仍然不完美:如果 2 个 goroutineCache.GetItem()同时调用,只有一个会尝试从客户端获取项目,但如果失败,只有这个 goroutine 会报告错误,另一个 goroutine 不会尝试获取来自客户端的项目,但将从地图加载它并且您不检查加载是否成功。你应该,如果它不在地图中,这意味着另一个并发尝试未能获得它。所以你应该报告错误(并清除sync.Once)。
如您所见,它变得越来越复杂。我坚持我之前的建议:sync.Mutex在这里使用 a 会更容易。
- 1 回答
- 0 关注
- 138 浏览
添加回答
举报