標籤:must faq exec ready 其他 cas when put 雜湊
go提供了一種叫map的資料結構,可以翻譯成映射,對應於其他語言的字典、雜湊表。藉助map,可以定義一個鍵和值,然後可以從map中擷取、設定和刪除這個值,尤其適合資料尋找的情境。但是map的使用有一定的限制,如果是在單個協程中讀寫map,那麼不會存在什麼問題,如果是多個協程並發訪問一個map,有可能會導致程式退出,並列印下面錯誤資訊:
fatal error: concurrent map read and map write
上面的這個錯誤不是每次都會遇到的,如果並發訪問的協程數不大,遇到的可能性就更小了。例如下面的程式:
1 package main 2 3 func main() { 4 Map := make(map[int]int) 5 6 for i := 0; i < 10; i++ { 7 go writeMap(Map, i, i) 8 go readMap(Map, i) 9 }10 11 }12 13 func readMap(Map map[int]int, key int) int {14 return Map[key]15 }16 17 func writeMap(Map map[int]int, key int, value int) {18 Map[key] = value19 }
只迴圈了10次,產生了20個協程並發訪問map,程式基本不會出錯,但是如果將迴圈次數變大,比如10萬,運行下面程式基本每次都會出錯:
1 package main 2 3 func main() { 4 Map := make(map[int]int) 5 6 for i := 0; i < 100000; i++ { 7 go writeMap(Map, i, i) 8 go readMap(Map, i) 9 }10 11 }12 13 func readMap(Map map[int]int, key int) int {14 return Map[key]15 }16 17 func writeMap(Map map[int]int, key int, value int) {18 Map[key] = value19 }
go官方部落格有如下說明:
Maps are not safe for concurrent use: it‘s not defined what happens when you read and write to them simultaneously. If you need to read from and write to a map from concurrently executing goroutines, the accesses must be mediated by some kind of synchronization mechanism. One common way to protect maps is with sync.RWMutex.
go FAQ解釋如下:
After long discussion it was decided that the typical use of maps did not require safe access from multiple goroutines, and in those cases where it did, the map was probably part of some larger data structure or computation that was already synchronized. Therefore requiring that all map operations grab a mutex would slow down most programs and add safety to few. This was not an easy decision, however, since it means uncontrolled map access can crash the program.
大致意思就是說,並發訪問map是不安全的,會出現未定義行為,導致程式退出。所以如果希望在多協程中並發訪問map,必須提供某種同步機制,一般情況下通過讀寫鎖sync.RWMutex實現對map的並發存取控制,將map和sync.RWMutex封裝一下,可以實現對map的安全並發訪問,範例程式碼如下:
1 package main 2 3 import "sync" 4 5 type SafeMap struct { 6 sync.RWMutex 7 Map map[int]int 8 } 9 10 func main() {11 safeMap := newSafeMap(10)12 13 for i := 0; i < 100000; i++ {14 go safeMap.writeMap(i, i)15 go safeMap.readMap(i)16 }17 18 }19 20 func newSafeMap(size int) *SafeMap {21 sm := new(SafeMap)22 sm.Map = make(map[int]int)23 return sm24 25 }26 27 func (sm *SafeMap) readMap(key int) int {28 sm.RLock()29 value := sm.Map[key]30 sm.RUnlock()31 return value32 }33 34 func (sm *SafeMap) writeMap(key int, value int) {35 sm.Lock()36 sm.Map[key] = value37 sm.Unlock()38 }
但是通過讀寫鎖控制map的並發訪問時,會導致一定的效能問題,不過能保證程式的安全運行,犧牲點效能問題是可以的。
參考
go官方部落格:https://blog.golang.org/go-maps-in-action
go FAQ:https://golang.org/doc/faq#atomic_maps
songleo
連結:https://www.jianshu.com/p/10a998089486
go語言學習--map的並發