go學習(10)併發讀寫訪問map問題

2021-08-28 15:28:39 字數 2371 閱讀 1269

golang 裡面 map 不是併發安全的,這一點是眾所周知的,而且官方文件也很早就給了解釋:why are map operations not defined to be atomic?. 也正如這個解釋說的一樣,要實現乙個併發安全的 map 其實非常簡單。

實際上,大多數情況下,對乙個 map 的訪問都是讀操作多於寫操作,而且讀的時候,是可以共享的。所以這種場景下,用乙個sync.rwmutex保護一下就是很好的選擇:

type syncmap struct 

sync.rwmutex

}

上面這個結構體定義了乙個併發安全的 string map,用乙個 map 來儲存資料,乙個讀寫鎖來保護安全。這個 map 可以被任意多的 goroutine 同時讀,但是寫的時候,會阻塞其他讀寫操作。新增上getsetdelete等方法,這個設計是能夠工作的,而且大多數時候能表現不錯。

但是這種設計會有些效能隱患。主要是兩個方面:

讀寫鎖的粒度太大了,保護了整個 map 的訪問。寫操作是阻塞的,此時其他任何讀操作都無法進行。

如果內部的 map 儲存了很多 key,gc 的時候就需要掃瞄很久。

一種解決思路是「分表」儲存,具體實現就是,基於上面的syncmap再包裝一次,用多個syncmap來模擬實現乙個 map:

type syncmap struct
上面這種設計用了乙個*syncmap的 slice 來儲存資料,shardcount提供了分表量的可定製性。實際上shards同樣可以實現為map[string]*syncmap

在這種設計下,資料(key:value)會被分散到不同的syncmap,而每個syncmap又有自己底層的 map。資料分散了,鎖也分散了,能夠很大程度上提高隨機訪問效能。而且在資料量大、高併發、寫操作頻繁的場景下,這種提公升會更加明顯。

那麼資料如何被分配到指定的分塊呢?一種很通用也很簡單的方法就是 hash. 字串的雜湊演算法有很多,byvoid 大神實現和比較了多種字串 hash 函式(各種字串hash函式比較),得出結論是:「bkdrhash無論是在實際效果還是編碼實現中,效果都是最突出的」。這裡採用了 bkdrhash 來實現:​​​​​​​

const seed uint32 = 131 // 31 131 1313 13131 131313 etc..

func bkdrhash(str string) uint32

return h

}// find the specific shard with the given key

func (m *syncmap) locate(key string) *syncmap

locate方法呼叫bkdrhash函式計算乙個key的雜湊值,然後用該值對分表量取模得到在 slice 的index,之後就能定位到對應的syncmap.

這種實現足夠簡單,而且也有不錯的效能表現。除了基本的getsetdelete等基本操作之外,迭代(range)功能也非常有用。更多的功能和細節,都可以在原始碼裡找到答案: 

還有一點:

如果業務場景能保證我們絕不會同時讀寫乙個key的話也不用加鎖,乙個小例子試驗下。。

會有衝突的情況:

package main

func main()

}func readmap(map map[int]int, key int) int

func writemap(map map[int]int, key int, value int)

go run -race main.go 結果如下:

那麼稍微改下呢。。

package main

func main()

}func readmap(map map[int]int, key int) int

func writemap(map map[int]int, key int, value int)

出現的是不衝突,但是沒啥意義,因為幾乎沒有這樣的應用場景吧,在這做這個小測試只是想說明golang 的map會出現讀寫衝突是因為map是引用型別,同時讀寫共享資源會使得共享資源崩潰

Go語言併發程式設計 讀寫鎖

通過對互斥鎖的學習,我們已經了解了鎖的概念及用途。主要用於處理併發中的臨界資源問題。rwmutex是基於mutex實現的,唯讀鎖的實現使用類似引用計數器的功能。rwmutext是讀 寫互斥鎖。鎖可以由任意數量的讀取器或單個編寫器持有。rwmutex的零值是未鎖定的mutex。當有乙個goroutin...

10 筆記go語言 併發

10.筆記go語言 併發 goroutine 是由 go 執行時環境管理的輕量級執行緒。go f x,y,z 開啟乙個新的 goroutine 執行 f x,y,z f x y 和 z 是當前 goroutine中定義的,但是在新的 goroutine 中執行 f goroutine 在相同的位址空...

go學習 goroutine併發學習總結

go最大的特性就是併發了,所以這一塊是go的重點知識,我自己花了一些時間,好好學習這個基礎知識。文章內容為個人學習理解,所以文章如果有不對之處,非常感謝指出。說起go併發,一般都會指go協程,即goroutine,而實現goroutin的關鍵字就是go。我學習go併發用到的關鍵字總結 sync.mu...