在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
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, 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 } go官方博客有如下说明:
go FAQ解释如下:
大致意思就是说,并发访问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的并发访问时,会导致一定的性能问题,不过能保证程序的安全运行,牺牲点性能问题是可以的。 参考
|
请发表评论