在具有昂贵的值结构的全局映射可能被多个并发线程修改的代码中,哪种模式是正确的?
// 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的并发模型?
发布于 2018-06-29 14:37:08
选项1显然可以由具有新ip的多个goroutines同时调用,并且只有if块中的最后一个将被存储。这种可能性随着buildComplexActivityObject时间的延长而大大增加,因为关键部分有更多的时间。
选项2有效,但每次都调用buildComplexActivityObject,这不是您想要的结果。
考虑到您希望尽可能频繁地调用buildComplexActivityObject,第三个选项是唯一有意义的选项。
但是,sync.Map不能保护存储指针引用的实际activity值。更新activity值时,还需要同步。
https://stackoverflow.com/questions/51103621
复制相似问题