go sync.map實現

golang map是非goroutine安全,若是多個goroutine使用map須要加鎖。但在高併發場景下,鎖的爭用會形成系統性能的降低。爲了解決這種問題,go 1.9以後提供了線程安全:sync.map。sync.map引入了兩個數據結構read,dirty來存儲,他們的底層都是用map來實現。golang

Golang選擇了 CAS 這種不用加鎖的方案來更新值,實現併發安全的map。

下面例舉了三個結構體安全

map: sync.map的結構體,包含read和dirty,read和dirty存儲了map中真實存儲的key/value值。misses表示從dirty讀取值的次數
readonly:Map.read值的結構體類型,m存儲key/value真實值。readOnly.amended表示read是否建立了dirty副本
entry: read和dirty中存儲value的指針數據結構

type Map struct {
    mu Mutex
    read atomic.Value // readOnly
    dirty map[interface{}]*entry
    misses int
}

type readOnly struct {
    m       map[interface{}]*entry
    amended bool 
}

type entry struct {
    p unsafe.Pointer // *interface{}
}

read是readOnly結構體,真實數據存儲在readOnly.m。併發

read和dirty的關聯:
image.png高併發

1: read至關於cache,讀取數據時,先從read讀取,沒有取到,從dirty讀取,Map.misses++。
當Map.misses達到dirty長度時,把dirty裏面的數據所有copy到read中,而且dirty置爲nil。性能

2: read和dirty map存儲的元素值是放在entry結構體中。read和dirty中相同key值指向同一個entry地址,因此當對read的key對應的value值進行修改,dirty中的值也會相應的被修改。atom

entry.p 的狀態:
1: nil表示entry被刪除,而且Map.dirty = nil
2: expunged(初始化的entry.p)表示entry被刪除,可是Map.dirty != nil
3: 其餘狀況表示值存在spa

snyc.Map主要提供了插入,查找,刪除操做,接下來會主要會講這三個方法的實現線程

插入流程
插入key, value
1: 先從read中獲取key,若是存在,而且這個key沒有被刪除,則直接更新read[key] = entry{p: value}返回
2: 不然,key存在可是被刪除了,在dirty中插入這個key,value值。dirty[key] = entry{p: value}返回
3: 若是dirty爲nil,則將read map的key,entry 添加到新建立的dirty map中;不爲nil,則跳過第3步
4: 將key, value插入dirty map中。dirty[key] = entry{p: value}指針

插入總結:
新加入的key值,會插入dirty中
之前存在,可是刪除過的key,會插入dirty中
之前存在,可是沒被刪除的key,read會更新這個key對應的value值,
因此 dirty不爲nil的時候,會全量保存key值。

查找流程
查找key
1: 從read中讀取到,直接返回
2: 沒有讀取到,而且dirty不爲nil,對map加鎖,而後再讀取一遍read map中內容(主要防止在加鎖的過程當中,有可能dirty map所有copy到read map,dirty置爲nil),若是read存在key,直接返回
3: read不存在,從dirty中讀取key對應的value值返回,而且map.misses++。當map.misses達到必定dirty長度,將dirty map所有copy到read map,dirty置爲nil。

查找總結:
讀read沒讀到,會從dirty中讀取,而且misses 次數+1,當次數達到必定dirty長度,會把dirty map所有copy到read map,dirty置爲nil。

刪除流程1: 從read中去讀key,若是存在,直接將從read[key]獲取到entry.p 置爲nil2: 不然,從dirty中刪除這個key值因此能夠得出,read刪除是直接把entry的p置爲nil,key保留。從dirty中刪除是直接刪除這個key

相關文章
相關標籤/搜索