欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

golang 并发访问map遇到的问题

程序员文章站 2024-03-19 14:32:04
...

go提供了一种叫map的数据结构,可以翻译成映射,对应于其他语言的字典、哈希表。借助map,可以定义一个键和值,然后可以从map中获取、设置和删除这个值,尤其适合数据查找的场景。但是map的使用有一定的限制,如果是在单个协程中读写map,那么不会存在什么问题,如果是多个协程并发访问一个map,有可能会导致程序退出,并打印下面错误信息:

fatal error: concurrent map read and map write

上面的这个错误不是每次都会遇到的,如果并发访问的协程数不大,遇到的可能性就更小了。例如下面的程序:

package main

func main() {
    Map := make(map[int]int)

    for i := 0; i < 10; i++ {
        go writeMap(Map, i, i)
        go readMap(Map, i)
    }

}

func readMap(Map map[int]int, key int) int {
    return Map[key]
}

func writeMap(Map map[int]int, key int, value int) {
    Map[key] = value
}

只循环了10次,产生了20个协程并发访问map,程序基本不会出错,但是如果将循环次数变大,比如10万,运行下面程序基本每次都会出错:

package main

func main() {
    Map := make(map[int]int)

    for i := 0; i < 100000; i++ {
        go writeMap(Map, i, i)
        go readMap(Map, i)
    }

}

func readMap(Map map[int]int, key int) int {
    return Map[key]
}

func writeMap(Map map[int]int, key int, value int) {
    Map[key] = value
}

 

大致意思就是说,并发访问map是不安全的,会出现未定义行为,导致程序退出。所以如果希望在多协程中并发访问map,必须提供某种同步机制,一般情况下通过读写锁sync.RWMutex实现对map的并发访问控制,将map和sync.RWMutex封装一下,可以实现对map的安全并发访问,示例代码如下:

package main

import "sync"

type SafeMap struct {
    sync.RWMutex
    Map map[int]int
}

func main() {
    safeMap := newSafeMap(10)

    for i := 0; i < 100000; i++ {
        go safeMap.writeMap(i, i)
        go safeMap.readMap(i)
    }

}

func newSafeMap(size int) *SafeMap {
    sm := new(SafeMap)
    sm.Map = make(map[int]int)
    return sm

}

func (sm *SafeMap) readMap(key int) int {
    sm.RLock()
    value := sm.Map[key]
    sm.RUnlock()
    return value
}

func (sm *SafeMap) writeMap(key int, value int) {
    sm.Lock()
    sm.Map[key] = value
    sm.Unlock()
}

但是通过读写锁控制map的并发访问时,会导致一定的性能问题,不过能保证程序的安全运行,牺牲点性能问题是可以的


转载链接:https://www.jianshu.com/p/10a998089486