91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用Go sync.Map

發布時間:2021-10-14 09:12:03 來源:億速云 閱讀:248 作者:iii 欄目:編程語言

這篇文章主要講解了“如何使用Go sync.Map”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何使用Go sync.Map”吧!

下面這段代碼,看起來很有道理,其實是用錯了(背景:并發場景中獲取注冊信息)。

instance, ok := instanceMap[name]if ok {
    return instance, nil}

initLock.Lock()defer initLock.Unlock()// double checkinstance, ok = instanceMap[name]if ok {
    return instance, nil}

這里使用使用 sync.Map 會更合理些,因為 sync.Map 底層完全包含了這個邏輯。可能寫 Java 的同學看著上面這段代碼很眼熟,但確實是用錯了,關于為什么用錯了以及會造成什么影響,請大家關注后續的文章。

我大概分析了下大家寧愿使用 Mutex + Map,也不愿使用 sync.Map 的原因:

  1. sync.Map 本身就很難用,使用起來并不像一個 Map。失去了 map 應有的特權語法,如:make,  map[1] 等

  2. sync.Map 方法較多。讓一個簡單的 Map 使用起來有了較高的學習成本。

不管什么樣的原因吧,當你讀過這篇文章后,在某些特定的并發場景下,建議使用 sync.Map 代替 Map + Mutex 的。

用法全解

package mainimport (
 "fmt" "sync")func main() {
 var syncMap sync.Map
 syncMap.Store("11", 11)
 syncMap.Store("22", 22)

 fmt.Println(syncMap.Load("11")) // 11 fmt.Println(syncMap.Load("33")) // 空 fmt.Println(syncMap.LoadOrStore("33", 33)) // 33 fmt.Println(syncMap.Load("33")) // 33 fmt.Println(syncMap.LoadAndDelete("33")) // 33 fmt.Println(syncMap.Load("33")) // 空 syncMap.Range(func(key, value interface{}) bool {
  fmt.Printf("key:%v value:%v\n", key, value)
  return true })
    // key:22 value:22 // key:11 value:11}

其實 sync.Map 并不復雜,只是將普通 map 的相關操作轉成對應函數而已。

 普通 mapsync.Map
map 獲取某個 keymap[1]sync.Load(1)
map 添加元素map[1] = 10sync.Store(1, 10)
map 刪除一個 keydelete(map, 1)sync.Delete(1)
遍歷 mapfor...rangesync.Range()

sync.Map 兩個特有的函數,不過從字面就能理解是什么意思了。LoadOrStore:sync.Map 存在就返回,不存在就插入 LoadAndDelete:sync.Map 獲取某個 key,如果存在的話,同時刪除這個 key

源碼解析

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

如何使用Go sync.Map

sync map 全景圖

如何使用Go sync.Map

Load

如何使用Go sync.Map

Store

如何使用Go sync.Map

Delete

read map 的值是什么時間更新的 ?

  1. Load/LoadOrStore/LoadAndDelete 時,當 misses 數量大于等于 dirty map 的元素個數時,會整體復制 dirty map 到 read map

  2. Store/LoadOrStore 時,當 read map 中存在這個key,則更新

  3. Delete/LoadAndDelete 時,如果 read map 中存在這個key,則設置這個值為 nil

dirty map 的值是什么時間更新的 ?

  1. 完全是一個新 key, 第一次插入 sync.Map,必先插入 dirty map

  2. Store/LoadOrStore 時,當 read map 中不存在這個key,在 dirty map 存在這個key,則更新

  3. Delete/LoadAndDelete 時,如果 read map 中不存在這個key,在 dirty map 存在這個key,則從 dirty map 中刪除這個key

  4. 當 misses 數量大于等于 dirty map 的元素個數時,會整體復制 dirty map 到 read map,同時設置 dirty map 為 nil

疑問:當 dirty map 復制到 read map 后,將 dirty map 設置為 nil,也就是 dirty map 中就不存在這個 key 了。如果又新插入某個 key,多次訪問后達到了 dirty map 往 read map 復制的條件,如果直接用 read map 覆蓋 dirty map,那豈不是就丟了之前在 read map 但不在 dirty map 的 key ?

答:其實并不會。當 dirty map 向 read map 復制后,readOnly.amended 等于了 false。當新插入了一個值時,會將 read map 中的值,重新給 dirty map 賦值一遍,也就是 read map 也會向 dirty map 中復制。

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

read map 和 dirty map 是什么時間刪除的?

  • 當 read map 中存在某個 key 的時候,這個時候只會刪除 read map, 并不會刪除 dirty map(因為 dirty map 不存在這個值)

  • 當 read map 中不存在時,才會去刪除 dirty map 里面的值

疑問:如果按照這個刪除方式,那豈不是 dirty map 中會有殘余的 key,導致沒刪除掉?

答:其實并不會。當 misses 數量大于等于 dirty map 的元素個數時,會整體復制 dirty map 到 read map。這個過程中還附帶了另外一個操作:將 dirty map 置為 nil。

func (m *Map) missLocked() {
 m.misses++
 if m.misses < len(m.dirty) {
  return }
 m.read.Store(readOnly{m: m.dirty})
 m.dirty = nil m.misses = 0}

read map 與 dirty map 的關系 ?

  1. 在 read map 中存在的值,在 dirty map 中可能不存在。

  2. 在 dirty map 中存在的值,在 read map 中也可能存在。

  3. 當訪問多次,發現 dirty map 中存在,read map  中不存在,導致 misses 數量大于等于 dirty map 的元素個數時,會整體復制 dirty map 到 read map。

  4. 當出現 dirty map 向 read map 復制后,dirty map 會被置成 nil。

  5. 當出現 dirty map 向 read map 復制后,readOnly.amended 等于了 false。當新插入了一個值時,會將 read map 中的值,重新給 dirty map 賦值一遍

read/dirty map 中的值一定是有效的嗎?

并不一定。放入到 read/dirty map 中的值總共有 3 種類型:

  • nil:如果獲取到的 value 是 nil,那說明這個 key 是已經刪除過的。既不在 read map,也不在 dirty map

  • expunged:這個 key 在 dirty map 中是不存在的

  • valid:其實就正常的情況,要么這個值存在在 read map 中,要么存在在 dirty map 中

sync.Map 是如何提高性能的?

通過源碼解析,我們知道 sync.Map 里面有兩個普通 map,read map主要是負責讀,dirty map 是負責讀和寫(加鎖)。在讀多寫少的場景下,read map 的值基本不發生變化,可以讓 read map 做到無鎖操作,就減少了使用 Mutex + Map 必須的加鎖/解鎖環節,因此也就提高了性能。

不過也能夠看出來,read map 也是會發生變化的,如果某些 key 寫操作特別頻繁的話,sync.Map 基本也就退化成了 Mutex + Map(有可能性能還不如 Mutex + Map)。

所以,不是說使用了 sync.Map 就一定能提高程序性能,我們日常使用中盡量注意拆分粒度來使用 sync.Map。

關于如何分析 sync.Map 是否優化了程序性能,同樣可以使用 pprof。具體過程可以參考 《這可能是最容易理解的 Go Mutex 源碼剖析》

sync.Map 應用場景

  1. 讀多寫少

  2. 寫操作也多,但是修改的 key 和讀取的 key 特別不重合。

關于第二點我覺得挺扯的,畢竟我們很難把控這一點,不過由于是官方的注釋還是放在這里。

實際開發中我們要注意使用場景和擅用 pprof 來分析程序性能。

sync.Map 使用注意點

和 Mutex 一樣, sync.Map 也同樣不能被復制,因為 atomic.Value 是不能被復制的。

感謝各位的閱讀,以上就是“如何使用Go sync.Map”的內容了,經過本文的學習后,相信大家對如何使用Go sync.Map這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

go
AI

资中县| 江孜县| 冀州市| 许昌市| 甘泉县| 邵东县| 张家口市| 会泽县| 木兰县| 新竹市| 商水县| 襄垣县| 聊城市| 得荣县| 东光县| 胶州市| 凤庆县| 新巴尔虎右旗| 子洲县| 哈巴河县| 瓦房店市| 永城市| 辉县市| 雷州市| 绥宁县| 石阡县| 镶黄旗| 蒲江县| 马山县| 虹口区| 灌云县| 综艺| 天峨县| 建阳市| 阳城县| 会理县| 南靖县| 黄陵县| 潢川县| 林周县| 故城县|