根據拓撲圖理解golang的sync.Map工做原理

sync.Map的拓撲關係圖

syncMap.png

讀懂關係圖須要瞭解的幾個關鍵點

var expunged = unsafe.Pointer(new(interface{}))
type Map struct 
type readOnly struct
type entry struct
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
func (m *Map) Store(key, value interface{})
func (m *Map) Delete(key interface{})
func (m *Map) Range(f func(key, value interface{}) bool)

這裏要重點關注readOnly.amendedMap.missesentry.p的數值狀態, 拓撲圖中,多處用於走勢判斷.
接下來詳細列出結構體的代碼和註釋, 方便閱讀理解拓撲圖.segmentfault

sync.Map主要結構和註釋

type Map struct {
    //互斥鎖,用於鎖定dirty map
    mu Mutex    
    
    //優先讀map,支持原子操做,註釋中有readOnly不是說read是隻讀,而是它的結構體。read實際上有寫的操做
    read atomic.Value 
    
    // dirty是一個當前最新的map,容許讀寫
    dirty map[interface{}]*entry 
    
    // 主要記錄read讀取不到數據加鎖讀取read map以及dirty map的次數,當misses等於dirty的長度時,會將dirty複製到read
    misses int 
}

// readOnly 主要用於存儲,經過原子操做存儲在 Map.read 中元素。
type readOnly struct {
    // read的map, 用於存儲全部read數據
    m       map[interface{}]*entry
    
    // 若是數據在dirty中但沒有在read中,該值爲true,做爲修改標識
    amended bool 
}

// entry 爲 Map.dirty 的具體map值
type entry struct {
    // nil: 表示爲被刪除,調用Delete()能夠將read map中的元素置爲nil
    // expunged: 也是表示被刪除,可是該鍵只在read而沒有在dirty中,這種狀況出如今將read複製到dirty中,即複製的過程會先將nil標記爲expunged,而後不將其複製到dirty
    //  其餘: 表示存着真正的數據
    p unsafe.Pointer // *interface{}
}
sync.Map的原理很簡單,使用了空間換時間策略,經過冗餘的兩個數據結構(read、dirty),實現加鎖對性能的影響。
經過引入兩個map將讀寫分離到不一樣的map,其中read map提供併發讀和已存元素原子寫,而dirty map則負責讀寫。
這樣read map就能夠在不加鎖的狀況下進行併發讀取,當read map中沒有讀取到值時,再加鎖進行後續讀取,並累加未命中數。
當未命中數大於等於dirty map長度,將dirty map上升爲read map。
從結構體的定義能夠發現,雖然引入了兩個map,可是底層數據存儲的是指針,指向的是同一份值。

參考文檔:
你不得不知道的sync.Map源碼分析數據結構

相關文章
相關標籤/搜索