如何使用Gosync.Map

這篇文章主要講解了“如何使用Go sync.Map”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“如何使用Go sync.Map”吧!

成都創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供騰沖網(wǎng)站建設(shè)、騰沖做網(wǎng)站、騰沖網(wǎng)站設(shè)計(jì)、騰沖網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、騰沖企業(yè)網(wǎng)站模板建站服務(wù),十年騰沖做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

下面這段代碼,看起來(lái)很有道理,其實(shí)是用錯(cuò)了(背景:并發(fā)場(chǎng)景中獲取注冊(cè)信息)。

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 會(huì)更合理些,因?yàn)?sync.Map 底層完全包含了這個(gè)邏輯??赡軐?Java 的同學(xué)看著上面這段代碼很眼熟,但確實(shí)是用錯(cuò)了,關(guān)于為什么用錯(cuò)了以及會(huì)造成什么影響,請(qǐng)大家關(guān)注后續(xù)的文章。

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

  1. sync.Map 本身就很難用,使用起來(lái)并不像一個(gè) Map。失去了 map 應(yīng)有的特權(quán)語(yǔ)法,如:make,  map[1] 等

  2. sync.Map 方法較多。讓一個(gè)簡(jiǎn)單的 Map 使用起來(lái)有了較高的學(xué)習(xí)成本。

不管什么樣的原因吧,當(dāng)你讀過(guò)這篇文章后,在某些特定的并發(fā)場(chǎng)景下,建議使用 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}

其實(shí) sync.Map 并不復(fù)雜,只是將普通 map 的相關(guān)操作轉(zhuǎn)成對(duì)應(yīng)函數(shù)而已。

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

sync.Map 兩個(gè)特有的函數(shù),不過(guò)從字面就能理解是什么意思了。LoadOrStore:sync.Map 存在就返回,不存在就插入 LoadAndDelete:sync.Map 獲取某個(gè) key,如果存在的話,同時(shí)刪除這個(gè) 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 的值是什么時(shí)間更新的 ?

  1. Load/LoadOrStore/LoadAndDelete 時(shí),當(dāng) misses 數(shù)量大于等于 dirty map 的元素個(gè)數(shù)時(shí),會(huì)整體復(fù)制 dirty map 到 read map

  2. Store/LoadOrStore 時(shí),當(dāng) read map 中存在這個(gè)key,則更新

  3. Delete/LoadAndDelete 時(shí),如果 read map 中存在這個(gè)key,則設(shè)置這個(gè)值為 nil

dirty map 的值是什么時(shí)間更新的 ?

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

  2. Store/LoadOrStore 時(shí),當(dāng) read map 中不存在這個(gè)key,在 dirty map 存在這個(gè)key,則更新

  3. Delete/LoadAndDelete 時(shí),如果 read map 中不存在這個(gè)key,在 dirty map 存在這個(gè)key,則從 dirty map 中刪除這個(gè)key

  4. 當(dāng) misses 數(shù)量大于等于 dirty map 的元素個(gè)數(shù)時(shí),會(huì)整體復(fù)制 dirty map 到 read map,同時(shí)設(shè)置 dirty map 為 nil

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

答:其實(shí)并不會(huì)。當(dāng) dirty map 向 read map 復(fù)制后,readOnly.amended 等于了 false。當(dāng)新插入了一個(gè)值時(shí),會(huì)將 read map 中的值,重新給 dirty map 賦值一遍,也就是 read map 也會(huì)向 dirty map 中復(fù)制。

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 是什么時(shí)間刪除的?

  • 當(dāng) read map 中存在某個(gè) key 的時(shí)候,這個(gè)時(shí)候只會(huì)刪除 read map, 并不會(huì)刪除 dirty map(因?yàn)?dirty map 不存在這個(gè)值)

  • 當(dāng) read map 中不存在時(shí),才會(huì)去刪除 dirty map 里面的值

疑問(wèn):如果按照這個(gè)刪除方式,那豈不是 dirty map 中會(huì)有殘余的 key,導(dǎo)致沒刪除掉?

答:其實(shí)并不會(huì)。當(dāng) misses 數(shù)量大于等于 dirty map 的元素個(gè)數(shù)時(shí),會(huì)整體復(fù)制 dirty map 到 read map。這個(gè)過(guò)程中還附帶了另外一個(gè)操作:將 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 的關(guān)系 ?

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

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

  3. 當(dāng)訪問(wèn)多次,發(fā)現(xiàn) dirty map 中存在,read map  中不存在,導(dǎo)致 misses 數(shù)量大于等于 dirty map 的元素個(gè)數(shù)時(shí),會(huì)整體復(fù)制 dirty map 到 read map。

  4. 當(dāng)出現(xiàn) dirty map 向 read map 復(fù)制后,dirty map 會(huì)被置成 nil。

  5. 當(dāng)出現(xiàn) dirty map 向 read map 復(fù)制后,readOnly.amended 等于了 false。當(dāng)新插入了一個(gè)值時(shí),會(huì)將 read map 中的值,重新給 dirty map 賦值一遍

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

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

  • nil:如果獲取到的 value 是 nil,那說(shuō)明這個(gè) key 是已經(jīng)刪除過(guò)的。既不在 read map,也不在 dirty map

  • expunged:這個(gè) key 在 dirty map 中是不存在的

  • valid:其實(shí)就正常的情況,要么這個(gè)值存在在 read map 中,要么存在在 dirty map 中

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

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

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

所以,不是說(shuō)使用了 sync.Map 就一定能提高程序性能,我們?nèi)粘J褂弥斜M量注意拆分粒度來(lái)使用 sync.Map。

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

sync.Map 應(yīng)用場(chǎng)景

  1. 讀多寫少

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

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

實(shí)際開發(fā)中我們要注意使用場(chǎng)景和擅用 pprof 來(lái)分析程序性能。

sync.Map 使用注意點(diǎn)

和 Mutex 一樣, sync.Map 也同樣不能被復(fù)制,因?yàn)?atomic.Value 是不能被復(fù)制的。

感謝各位的閱讀,以上就是“如何使用Go sync.Map”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)如何使用Go sync.Map這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

分享文章:如何使用Gosync.Map
新聞來(lái)源:http://bm7419.com/article24/jdieje.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、手機(jī)網(wǎng)站建設(shè)、關(guān)鍵詞優(yōu)化全網(wǎng)營(yíng)銷推廣、自適應(yīng)網(wǎng)站、云服務(wù)器

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營(yíng)