Goland sync.Map大白話解析
阿新 • • 發佈:2022-03-07
Goland sync.Map大白話解析
程式碼解析連結:https://mp.weixin.qq.com/s/H5HDrwhxZ_4v6Vf5xXUsIg
建議對照參考連結程式碼食用
結構體
可以簡單理解為:sync包中的Map結構體裡面有兩個map,分別是read和dirty,read和dirty的在結構上的最大不同點,就是read在dirty的基礎上多了一個amended欄位,用來表示dirty中是否存在read沒有的資料。
其中read和dirty中的value值都是一個entry結構體,結構體中存放著指向該值的指標pointer,pointer有三種值,分別是nil,expunged,真正指向值的指標。nil是真正刪除了,expunged是軟刪除。
另外還有一個misses欄位和互斥鎖mu,misses表示穿透了read直接命中dirty的次數。
總結:
sync.Map其實是把資料分成了讀和寫兩個區域,從而減少了每次獲取都要加鎖的額外開銷。
函式介紹
Load方法
Load(key interface{}) (value interface{}, ok bool)
獲取Key值,返回對應的value和value存在與否
- 讀readMap,如果讀到了直接返回結果
- 獲取不到,判斷readMap中amended欄位(用來表示dirtyMap中是否含有readMap沒有的資料),如果amended為true,說明dirtyMap中含有readMap
- 加鎖(全域性鎖),再讀readMap(雙重校驗),讀到了entry就返回結果
- 再次就那些2的判斷,如果為false,說明真的沒有資料,返回nil和false。如果為true,再去讀dirtyMap
- 從dirtyMap獲取entry,獲取不到,返回nil和false,獲取到了則返回對應的value和true,並且會對misses欄位進行+1操作,如果misses欄位大於等於dirtyMap長度,則把dirtyMap置換為read的map(相當於把dirtyMap賦值給readMap),並且重置dirtyMap,把misses
- (上述的返回資料只是用臨時變數去存放資料,並沒有真正返回,最後才真正返回)釋放鎖,返回資料。
總結:
- 先讀readMap,獲取不到再加鎖。然後雙重校驗再次讀readMap,讀不到再去訪問dirtyMap。
- 訪問readMap不存在但dirtyMap存在的資料,會帶來加鎖的額外開銷。
Store方法
- 讀readMap,如果讀到了entry,並且值的指標不是expunged(軟刪除),則更新值,返回資料
- 如果讀不到entry,或者值已經被軟刪除,則加鎖,再次讀readMap,雙重校驗。
- 如果在readMap讀到了entry,並且值已經被軟刪除,則把entry.p的expunged替換為nil,並且在dirtyMap中新增此key和entry,然後更新entry的值,釋放鎖,返回。
- 如果在readMap讀不到entry,則去讀dirtyMap,如果在dirtyMap中讀到了entry,則執行更新值的操作,並釋放鎖返回。
- 如果dirtyMap中也不存在此值,並且readMap的amended欄位為false(dirtyMap中不含有readMap沒有的資料),(如果dirtyMap等於nil,則把readMap不為expunged和不為nil的元素新增到dirty中),把amended設定為true,因為現在dirtyMap中有readMap不存在的資料,把新值新增到dirty中,釋放鎖,返回。
- 如果dirtyMap不存在此值,並且amended為true,則把新值新增到dirtyMap中,釋放鎖,返回。相比於步驟5,減少了把readMap中entry.p != expunged&&entry.p != nil的元素新增到dirtyMap中的步驟。
總結:
- Store方法優先無鎖訪問readMap,未命中會加鎖訪問dirtyMap
- 加鎖訪問後,會把新的元素新增到dirtyMap中,並把readMap的ammend元素設定為true,用Load函式去獲取該元素,會導致加鎖訪問dirtyMap。並且只有到了未命中次數等於dirtyMap長度以後(Load和Delete方法都會有此檢測),才會把dirtyMap升級為readMap,此後Load函式才會直接訪問readMap
- 所以說,sync.Map不適合頻繁插入新元素的場景,這會導致頻繁加鎖訪問dirtyMap,帶來額外的效能開銷。
Delete方法
- 讀readMap,如果讀不到entry並且amended為true(說明dirtyMap存在),加鎖,再次讀readMap,雙重校驗,如果還是讀不到entry並且amended為true,則去讀dirtyMap,把dirtyMap中存在的值刪掉,misses欄位加一,如果misses欄位大於等於dirtyMap長度,把dirtyMap升級為readMap,dirtyMap設為nil,miss設為0,返回
- 讀readMap,如果讀到了entry或者amended為false,如果entry.p為nil或者expunged,則直接返回,否則把entry.p設為expunged(軟刪除),返回。
總結:
- 刪除readOnly中存在的key,可以不用加鎖
- 如果刪除readOnly中不存在的或者Map中不存在的key,都需要加鎖