go语言的sync.map源码学习

  1. amended标识read里数据是否已被复制到dirty
  2. dirty存储map里所有的数据,只有当miss次数>=dirty长度了,才会把read的m字段指向dirty数据,同时清空dirty,设置amended为false.
  3. 查询时read里查到值的话:返回时判断是否是nil或expunged,是的话就返回nil,不是的话就返回值。
  4. 查询时read里没查到的话:返回的就是dirty里查的
  5. 删除时read里查到值的话:删除只是把val置为nil,后续将read复制到dirty的时候,会将val置为expunged.
  6. 删除时read里没查到值的话:删除是在dirty里删除的。
  7. 插入时read里查到的话就通过cas操作直接赋值。
  8. 插入时read里没有的话就会加锁,然后再次从read里读取,此时read里有的话会判断val是否是expunged,是的话则将expunged通过cas操作赋值为nil并把要插入的kv插入到dirty中。不是expunged的话则直接原子性赋值。read里没有dirty里查到的话,直接通过原子性操作更改值。read里没有dirty里也没有的话,先判断read是否被复制到dirty里过,如果没有的话则先将read里非nil和非expunged的kv复制到dirty里(此过程会同时将read里的v为nil的都换成expunged,标识此k在dirty不存在,且dirty不为nil.),然后想dirty里插入此kv对。


k就是代码里的key,v就是代码里的value

  • Load方法:

func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
    //首先查询map的read属性上是否有值,
    read, _ := m.read.Load().(readOnly)
    e, ok := read.m[key]
    //没有值且read已经被复制了
    if !ok && read.amended {
        m.mu.Lock()
        // Avoid reporting a spurious miss if m.dirty got promoted while we were
        // blocked on m.mu. (If further loads of the same key will not miss, it's
        // not worth copying the dirty map for this key.)
        read, _ = m.read.Load().(readOnly)
        e, ok = read.m[key]
        if !ok && read.amended {
            //从dirty里查询值
            e, ok = m.dirty[key]
            // Regardless of whether the entry was present, record a miss: this key
            // will take the slow path until the dirty map is promoted to the read
            // map.
            //记录一次miss
            m.missLocked()
        }
        m.mu.Unlock()
    }
    if !ok {
        return nil, false
    }
    return e.load()
}

func (m *Map) missLocked() {
    m.misses++
    if m.misses < len(m.dirty) {
        return
    }
    //此时miss数大于等于dirty长度了,将read指向dirty,然后dirty赋值空
    m.read.Store(readOnly{m: m.dirty})
    m.dirty = nil
    m.misses = 0
}
//这个方法很重要,这个方法是说在read里查到了这个key,返回值的时候,会判断是否是nil或expunged,然后再回返回值。
func (e *entry) load() (value interface{}, ok bool) {
   p := atomic.LoadPointer(&e.p)
   if p == nil || p == expunged {
      return nil, false
   }
   return *(*interface{})(p), true
}
  • 插入值

func (m *Map) Store(key, value interface{}) {
    //首先查询read中是否含有此key,有的话cas尝试赋值,失败则上锁
    read, _ := m.read.Load().(readOnly)
    if e, ok := read.m[key]; ok && e.tryStore(&value) {
        return
    }


    m.mu.Lock()
    read, _ = m.read.Load().(readOnly)
    if e, ok := read.m[key]; ok {
        //此key之前已被删除,且不在dirty里,cas操作把它置为nil。且将k、v插入dirty里
        if e.unexpungeLocked() {
            // The entry was previously expunged, which implies that there is a
            // non-nil dirty map and this entry is not in it.
            m.dirty[key] = e
        }
        //代表之前未被删除,在read里修改它的值
        e.storeLocked(&value)
    } else if e, ok := m.dirty[key]; ok {
        //这个key在dirty里,直接赋值
        e.storeLocked(&value)
    } else {
        //此时read没有被复制,dirty还是空的,所以先调用m.dirtyLocked()将read里未被删除的复制到dirty里
        if !read.amended {
            // We're adding the first new key to the dirty map.
            // Make sure it is allocated and mark the read-only map as incomplete.
            //将read里的非nil和非expunged的kv复制到dirty里,且所有的v为nil的都通过cas操作赋值成expunged
            m.dirtyLocked()
            m.read.Store(readOnly{m: read.m, amended: true})
        }
        m.dirty[key] = newEntry(value)
    }
    m.mu.Unlock()
}

func (m *Map) dirtyLocked() {
   if m.dirty != nil {
      return
   }


   read, _ := m.read.Load().(readOnly)
   m.dirty = make(map[interface{}]*entry, len(read.m))
   for k, e := range read.m {
      if !e.tryExpungeLocked() {
         m.dirty[k] = e
      }
   }
}


func (e *entry) tryExpungeLocked() (isExpunged bool) {
   p := atomic.LoadPointer(&e.p)
   for p == nil {
      if atomic.CompareAndSwapPointer(&e.p, nil, expunged) {
         return true
      }
      p = atomic.LoadPointer(&e.p)
   }
   return p == expunged
}


func (e *entry) storeLocked(i *interface{}) {
    atomic.StorePointer(&e.p, unsafe.Pointer(i))
}
  • 删除

func (m *Map) Delete(key interface{}) {
   read, _ := m.read.Load().(readOnly)
   e, ok := read.m[key]
   if !ok && read.amended {
      m.mu.Lock()
      read, _ = m.read.Load().(readOnly)
      e, ok = read.m[key]
      if !ok && read.amended {
         delete(m.dirty, key)
      }
      m.mu.Unlock()
   }
   if ok {
      e.delete()
   }
}
//删除只是把v置为nil,后续将read复制到dirty的时候,会将v置为expunged.
func (e *entry) delete() (hadValue bool) {
   for {
      p := atomic.LoadPointer(&e.p)
      if p == nil || p == expunged {
         return false
      }
      if atomic.CompareAndSwapPointer(&e.p, p, nil) {
         return true
      }
   }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章