go语言中map并发读写 go map 并发读写

Go Map 为什么是非线程安全的? Go map 默认是并发不安全的go语言中map并发读写,同时对 map 进行并发读写的时go语言中map并发读写,程序会 panic , 原因如下:Go 官方经过长时间的讨论 , 认为 map 适配的场景应该是简单的(不需要从多个 gorountine 中进行安全访问的),而不是为go语言中map并发读写了小部分情况(并发访问),导致大部分程序付出锁的代价,因此决定了不支持 。
并发读写可能引发的问题
使用sync.RWMutex解决并发读写的问题
使用 sync.Map 解决并发读写的问题
实际上,sync.Map也是通过加锁的方式实现并发安全的,sync.Map源码的数据结构如下:
就像官方考虑的那样,我们在使用中,应尽量避免对 Map 进行并发读写,尝试通过其他方式解决问题,如数据解耦、分布执行、动态规划等 , 真的需要并发读写时,为避免产生并发读写的问题,请使用锁的机制进行控制
Golang中sync.Map的实现原理前面,我们讲了map的用法以及原理 Golang中map的实现原理,但我们知道,map在并发读写的情况下是不安全 。需要并发读写时,一般的做法是加锁,但这样性能并不高,Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,今天,我们就来讲讲 sync.Map的用法以及原理
sync.Map与map不同 , 不是以语言原生形态提供,而是在 sync 包下的特殊结构:
我们下来看下sync.Map结构体
结构体之间的关系如下图所示:
总结一下:
Load方法比较简单 , 总结一下:
总结如下:
Golang 并发读写map安全问题详解 下面先写一段测试程序,然后看下运行结果:
运行结果:
发生了错误,提示:fatal error: concurrent map read and map write,map 发生了同时读和写了; 但是这个错误并不是每次运行都会出现,就是有的时候会出现,有的时候并不会出现,根据笔者多次运行结果(其他例子,读者可以自己尝试下)来看还会有另外一种报错就是:fatal error: concurrent map writes,就是map发生了同时写,但是只是读是不会有问题的 。关于不同的运行结果小伙伴们可以自己写几个例子去测试下 。下面就这两个错误的发生,笔者给出如下解释:
(1) fatal error: concurrent map read and map write
就是当一个goroutine在写数据,而同时另外一个goroutine要读数据就会报错 , 不过这个报错也很好理解:还没写完就读,读的数据会有问题,或者反过来还没读完就开始写了,同样会导致读取的数据有问题;
(2) fatal error: concurrent map writes
两个goroutine 同时写一个内存地址,这种操作也是不允许的,会导致一些比较奇怪的问题;
总体来看其实就是写map的操作和其他的读或者写同时发生了,导致的报错 , 做过几年开发的人可能会想到使用锁来解决,比如写map某个key的时候 , 通过锁来保证其他goroutine不能再对其写或者读了 。
实现思路:
(1) 当写map的某个key时,通过锁来保证其他goroutine不能再对其写或者读了 。
(2) 当读map的某个key时,通过锁来保证其他的goroutine不能再对其写,但是可以读 。
于是我们马上想到golang 的读写锁貌似符合需求 , 下面来实现下:
再来看下运行结果:
发现没有报错了,并且多次运行的结果都不会报错 , 说明这个方法是有用的,不过在go1.9版本后就有sync.Map了 , 不过这个适用场景是读多写少的场景,如果写很多的话效率比较差,具体的原因在这里笔者就不介绍了,后面会写篇文章详细介绍下 。

推荐阅读