1. 程式人生 > 其它 >Goland sync.Map大白話解析

Goland sync.Map大白話解析

Goland sync.Map大白話解析

程式碼解析連結:https://mp.weixin.qq.com/s/H5HDrwhxZ_4v6Vf5xXUsIg

建議對照參考連結程式碼食用

結構體

可以簡單理解為:sync包中的Map結構體裡面有兩個map,分別是readdirtyreaddirty的在結構上的最大不同點,就是readdirty的基礎上多了一個amended欄位,用來表示dirty中是否存在read沒有的資料。

其中readdirty中的value值都是一個entry結構體,結構體中存放著指向該值的指標pointer,pointer有三種值,分別是nilexpunged,真正指向值的指標。nil是真正刪除了,expunged是軟刪除。

另外還有一個misses欄位和互斥鎖mumisses表示穿透了read直接命中dirty的次數。

總結:

sync.Map其實是把資料分成了讀和寫兩個區域,從而減少了每次獲取都要加鎖的額外開銷。

函式介紹

Load方法

Load(key interface{}) (value interface{}, ok bool) 獲取Key值,返回對應的value和value存在與否

  1. readMap,如果讀到了直接返回結果
  2. 獲取不到,判斷readMapamended欄位(用來表示dirtyMap中是否含有readMap沒有的資料),如果amendedtrue,說明dirtyMap中含有readMap
    沒有的資料,如果為false,說明不存在此資料,返回nil和false
  3. 加鎖(全域性鎖),再讀readMap(雙重校驗),讀到了entry就返回結果
  4. 再次就那些2的判斷,如果為false,說明真的沒有資料,返回nil和false。如果為true,再去讀dirtyMap
  5. dirtyMap獲取entry,獲取不到,返回nil和false,獲取到了則返回對應的value和true,並且會對misses欄位進行+1操作,如果misses欄位大於等於dirtyMap長度,則把dirtyMap置換為read的map(相當於把dirtyMap賦值給readMap),並且重置dirtyMap,把misses
    設定為0,把amended欄位設定為false,表示dirtyMap中不存在readMap沒有的資料
  6. (上述的返回資料只是用臨時變數去存放資料,並沒有真正返回,最後才真正返回)釋放鎖,返回資料。

總結:

  1. 先讀readMap,獲取不到再加鎖。然後雙重校驗再次讀readMap,讀不到再去訪問dirtyMap
  2. 訪問readMap不存在但dirtyMap存在的資料,會帶來加鎖的額外開銷。

Store方法

  1. readMap,如果讀到了entry,並且值的指標不是expunged(軟刪除),則更新值,返回資料
  2. 如果讀不到entry,或者值已經被軟刪除,則加鎖,再次讀readMap,雙重校驗。
  3. 如果在readMap讀到了entry,並且值已經被軟刪除,則把entry.p的expunged替換為nil,並且在dirtyMap中新增此key和entry,然後更新entry的值,釋放鎖,返回。
  4. 如果在readMap讀不到entry,則去讀dirtyMap,如果在dirtyMap中讀到了entry,則執行更新值的操作,並釋放鎖返回。
  5. 如果dirtyMap中也不存在此值,並且readMap的amended欄位為false(dirtyMap中不含有readMap沒有的資料),(如果dirtyMap等於nil,則把readMap不為expunged和不為nil的元素新增到dirty中),把amended設定為true,因為現在dirtyMap中有readMap不存在的資料,把新值新增到dirty中,釋放鎖,返回。
  6. 如果dirtyMap不存在此值,並且amended為true,則把新值新增到dirtyMap中,釋放鎖,返回。相比於步驟5,減少了把readMap中entry.p != expunged&&entry.p != nil的元素新增到dirtyMap中的步驟。

總結:

  1. Store方法優先無鎖訪問readMap,未命中會加鎖訪問dirtyMap
  2. 加鎖訪問後,會把新的元素新增到dirtyMap中,並把readMap的ammend元素設定為true,用Load函式去獲取該元素,會導致加鎖訪問dirtyMap。並且只有到了未命中次數等於dirtyMap長度以後(Load和Delete方法都會有此檢測),才會把dirtyMap升級為readMap,此後Load函式才會直接訪問readMap
  3. 所以說,sync.Map不適合頻繁插入新元素的場景,這會導致頻繁加鎖訪問dirtyMap,帶來額外的效能開銷。

Delete方法

  1. readMap,如果讀不到entry並且amended為true(說明dirtyMap存在),加鎖,再次讀readMap,雙重校驗,如果還是讀不到entry並且amended為true,則去讀dirtyMap,把dirtyMap中存在的值刪掉,misses欄位加一,如果misses欄位大於等於dirtyMap長度,把dirtyMap升級為readMapdirtyMap設為nil,miss設為0,返回
  2. readMap,如果讀到了entry或者amended為false,如果entry.p為nil或者expunged,則直接返回,否則把entry.p設為expunged(軟刪除),返回。

總結:

  1. 刪除readOnly中存在的key,可以不用加鎖
  2. 如果刪除readOnly中不存在的或者Map中不存在的key,都需要加鎖