一尘不染

在sync.Map中,必须对复杂值使用Load和LoadOrStore

go

在代码中,可以通过多个并发线程修改具有昂贵的生成价值结构的全局映射的哪个模式是正确的?

// 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的并发模型,第三版是否是正确的模式?


阅读 582

收藏
2020-07-02

共1个答案

一尘不染

选项1显然可以被多个goroutine调用ip,并带有一个新的并发调用,并且只有if块中的最后一个会被存储。由于buildComplexActivityObject在关键部分有更多的时间,因此花费的时间越长,这种可能性就会大大增加。

选项2有效,但是buildComplexActivityObject每次都调用,而您声明的状态不是您想要的。

鉴于您希望buildComplexActivityObject不经常拨打电话,第三个选项是唯一有意义的选项。

sync.Map但是不能保护实际activity被存储的指针的参考价值。在更新activity值时,您还需要在那里进行同步。

2020-07-02