go語言學習--map的並發
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] = value 19 }
只循環了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, keyint) int { 14 return Map[key] 15 } 16 17 func writeMap(Map map[int]int, key int, value int) { 18 Map[key] = value 19 }
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 sm 24 25 } 26 27 func (sm *SafeMap) readMap(key int) int { 28 sm.RLock() 29 value := sm.Map[key] 30 sm.RUnlock() 31 return value 32 } 33 34 func (sm *SafeMap) writeMap(key int, value int) { 35 sm.Lock() 36 sm.Map[key] = value 37 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的並發