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
      }
   }
}

 

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