Go-解决go的fatal error: concurrent map writes map非并发安全
普及概念:
普及1
1 | package main |
上述方法,其实就是通过sleep等待go并行线程都完成再往下走,但是这个也是有问题的因为sleep的时间无法精确。
- 可以考虑使用管道来完成上述操作:
1 | func main() { |
首先可以肯定的是使用管道是能达到我们的目的的,而且不但能达到目的,还能十分完美的达到目的。
- 但go语言中有一个其他的工具
sync.WaitGroup能更加方便的帮助我们达到这个目的。
WaitGroup 对象内部有一个计数器,最初从0开始,它有三个方法:Add(), Done(), Wait() 用来控制计数器的数量。Add(n) 把计数器设置为n ,Done() 每次把计数器-1 ,wait() 会阻塞代码的运行,直到计数器地值减为0。
1 | func main() { |
但是要注意:
1.我们不能使用Add() 给wg 设置一个负值
2.WaitGroup对象不是一个引用类型
WaitGroup对象不是一个引用类型,在通过函数传值的时候需要使用地址:
1 | func main() { |
普及2
Go语言的 defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。
1 | 好处及用处就不用说了。 |
普及3
golang中sync.RWMutex和sync.Mutex区别
golang中sync包实现了两种锁Mutex (互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能.
其中Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁叶叫做全局锁。
RWMutex是一个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景.
- 1、读锁的时候无需等待读锁的结束
- 2、读锁的时候要等待写锁的结束
- 3、写锁的时候要等待读锁的结束
- 4、写锁的时候要等待写锁的结束
正题
问题代码
1 | var wg sync.WaitGroup |
为什么会报”fatal error: concurrent map writes map”?
因为map不是并发安全的 , 当有多个并发的groutine读写同一个map时,会出现panic错误。
解决方案
在继续使用Map类型的情况下,常规解决方案一般分为两种。
- 并发的groutine定义多个map,进行读写。这样就不存在同时读写一个map。这种的试用场景就是,
1 | a := make(map[int]string) |
- 但更多是通过锁(适合上面代码)
1 | var wg sync.WaitGroup |
这样就可以有效解决报错问题。但是会无形造成串行,因为都在等待解锁。
使用并行的解决方案
- 所以,我们想了两种方案,一种弃用map类型(这个该文章不描述),另一种使用sync.Map(版本支持)。
1 | 因为map本就是不适合并行的,所以go官方推出了sync.Map。 |
结果:elapsed为8.005294657s,证明可以并行。
- 但是sync.Map和Map的使用却是有很大区别,下面介绍sync.Map的使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var sortUrl sync.Map //定义
sortUrl.Store(i, urlString) //使用i为key,urlString为value
// 遍历所有sync.Map中的键值对
sortUrl.Range(func(k, v interface{}) bool {
key := k.(int) //注意转换成int类型
keys = append(keys, key)
return true
})
for k := range keys {
key,_ := sortUrl.Load(k) //获取值,返回value interface{}, ok bool
keyValue := key.(string) //转成string类型
uploadSuccessUrl = append(uploadSuccessUrl, keyValue)
}
更多操作可参考文档:Go语言sync.Map (在并发环境中使用的map)
附录介绍一些需要的类型转换文档:
Golang 中整数转字符串的方法
golang-interface转string
golang学习之interface与其它类型转换

