go - 在 sync.Map 中是否有必要使用 Load 后跟 LoadOrStore 来处理复杂值
问题描述
在代码中,具有昂贵的生成值结构的全局映射可能会被多个并发线程修改,哪种模式是正确的?
// equivalent to map[string]*activity where activity is a
// fairly heavyweight structure
var ipActivity sync.Map
// version 1: not safe with multiple threads, I think
func incrementIP(ip string) {
val, ok := ipActivity.Load(ip)
if !ok {
val = buildComplexActivityObject()
ipActivity.Store(ip, val)
}
updateTheActivityObject(val.(*activity), ip)
}
// version 2: inefficient, I think, because a complex object is built
// every time even through it's only needed the first time
func incrementIP(ip string) {
tmp := buildComplexActivityObject()
val, _ := ipActivity.LoadOrStore(ip, tmp)
updateTheActivity(val.(*activity), ip)
}
// version 3: more complex but technically correct?
func incrementIP(ip string) {
val, found := ipActivity.Load(ip)
if !found {
tmp := buildComplexActivityObject()
// using load or store incase the mapping was already made in
// another store
val, _ = ipActivity.LoadOrStore(ip, tmp)
}
updateTheActivity(val.(*activity), ip)
}
考虑到 Go 的并发模型,第三版是正确的模式吗?
解决方案
选项 1 显然可以由多个 goroutine 同时调用一个新的ip
,并且只有if
块中的最后一个会被存储。这种可能性随着时间的延长而大大增加buildComplexActivityObject
,因为在关键部分有更多的时间。
buildComplexActivityObject
选项 2 有效,但每次都调用,你说这不是你想要的。
鉴于您想buildComplexActivityObject
尽可能不频繁地调用,第三种选择是唯一有意义的选择。
但是sync.Map
不能保护activity
存储的指针引用的实际值。activity
更新值时,您还需要在那里同步。
推荐阅读
- phaser-framework - Phaser 可滚动文本框教程不适用于移动设备
- javascript - 如何在 React 中获取嵌入式 Monaco 编辑器的行数?(包括包装)
- json - 在 Swift 中从 json 到 struct 的解码失败
- python - 数据类:super() .__ init __ () 的继承等价物是什么?
- html - ipfs 节点无法连接到我在本地主机上运行的 react 应用程序
- reactjs - 单击时更改 Material UI IconButton 图标
- laravel - Laravel 8 查询生成器 WhereIn Like
- android - 我多次生成 apk 并且他没有以前的代码更改,但是在模拟器 android、iOS 和 iOS 财务设备中,更改显示了它
- reactjs - 如何从数据库 firebase 获取和渲染点
- python - 我怎样才能找到幂零矩阵?