此文章记录再 golang 中goroutine开发遇到的并发问题问题及其解决方案

map 在并发中的安全问题

fatal error: concurrent map read and map write
导致的原因:

go内置的map不是线程安全的,如果原生map保证并发安全,那么一些不需要并发的场景,会有不小的性能损耗。

笼统的来说,go的map底层是一个hash表(HashMap),表面上看map只有键值对结构,
实际上在存储键值对的过程中涉及到了数组和链表。HashMap之所以高效,
是因为其结合了顺序存储(数组)和链式存储(链表)两种存储结构。数组是HashMap的主干,
在数组下有一个类型为链表的元素。
哈希函数会将传入的key值进行哈希运算,得到一个唯一的值

上述描述是 网络上查询到的解释,个人理解是:
map 是key-value 存储 在一个 数组 内,以key进行hash 运算后的值作为地址存储进去,
hash 运算后的值存在 hash 冲突(可能多个 key hash运算得到的结果一致),
这样就把 hash 冲突的值以 链表的 形式存储在 这个 key 后面。
上面理解可能存在错误,欢迎指正。

go map 为什么并发不安全,我查询的解释是 go 语言 map 并发读写会报个 panic 错误。
对应其并发安全我的理解是,并发中对 map 进行读写操作 会同时 读写 到 hash运算冲突的值,
这样存储的 话 就有问题了,链式存储会 在同一个 hash key 存储两个值 且这两个值间没有 链式关系结构。
上面理解可能存在错误,欢迎指正。

解决方案:

# 最简单的是 新增 map 的读写方法,再每次读写操作时进行加锁,保证同时只有一个线程可以读写 map
var mapData = make(map[string]int)
var mapLock sync.RWMutex

func getValue(key string) int {
	mapLock.RLock()
	defer mapLock.RUnlock()
	return mapData[key]
}


func setValue(key string,value int) {
	mapLock.Lock()
	defer mapLock.Unlock()
	mapData[key]=value
}

注:go 里面 除了map 并发不安全外,slice 并发也不安全。不过 slice 并发时不会报错,slice 的并发不安全是 多个线程存在同时读到 slice 的一个地址,这就会导致 slice 值的覆盖