转载自:
https://blog.csdn.net/u010230794/article/details/82143179
https://www.jianshu.com/p/10a998089486
sync.Map的使用:
package main
import (
"sync"
"fmt"
)
func main() {
//开箱即用
var sm sync.Map
//store 方法,添加元素
sm.Store(1,"a")
//Load 方法,获得value
if v,ok:=sm.Load(1);ok{
fmt.Println(v)
}
//LoadOrStore方法,获取或者保存
//参数是一对key:value,如果该key存在且没有被标记删除则返回原先的value(不更新)和true;不存在则store,返回该value 和false
if vv,ok:=sm.LoadOrStore(1,"c");ok{
fmt.Println(vv)
}
if vv,ok:=sm.LoadOrStore(2,"c");!ok{
fmt.Println(vv)
}
//遍历该map,参数是个函数,该函数参的两个参数是遍历获得的key和value,返回一个bool值,当返回false时,遍历立刻结束。
sm.Range(func(k,v interface{})bool{
fmt.Print(k)
fmt.Print(":")
fmt.Print(v)
fmt.Println()
return true
})
}
利用传统的sync.RWMutex+Map 实现并发安全的map:
var rwmap = struct{
sync.RWMutex
m map[string]string
}{m: make(map[string]sring)}
读数据时候,读锁锁定:
rwmap.RLock()
value:= rwmap.m["key"]
rwmap.RUnlock()
fmt.Println("key:", value)
写数据的时候,写锁锁定:
rwmap.Lock()
rwmap.m["key"]="value"
rwmap.Unlock()
下面是有人做的两种map性能对比图:
可见随着cpu核心数的增加、并发加剧,这种读写锁+map的方式性能在不停的衰减,并且在核数为4的时候出现了性能的拐点;而sync.Map虽然性能不是特别好,但是相对比较平稳。
使用场景:
在服务器中,假设有一个存放用户信息的Map,在对这个Map进行操作时(如加入某个用户信息或去除某个用户信息),多协程对数据进行修改可能会导致数据竞争,因此需要使用线程安全的Map,如果使用普通Map的话会引起panic:
package main
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
}
func main() {
Map := make(map[int]int)
for i := 0; i < 100000; i++ {
go writeMap(Map, i, i)
go readMap(Map, i)
}
}
go官方博客有如下说明:
Maps are not safe for concurrent use: it's not defined what happens when you read and write to them simultaneously. If you need to read from and write to a map from concurrently executing goroutines, the accesses must be mediated by some kind of synchronization mechanism. One common way to protect maps is with sync.RWMutex.
go FQA解释如下:
After long discussion it was decided that the typical use of maps did not require safe access from multiple goroutines, and in those cases where it did, the map was probably part of some larger data structure or computation that was already synchronized. Therefore requiring that all map operations grab a mutex would slow down most programs and add safety to few. This was not an easy decision, however, since it means uncontrolled map access can crash the program.
大致意思就是说,并发访问map是不安全的,会出现未定义行为,导致程序退出。所以如果希望在多协程中并发访问map,必须提供某种同步机制,一般情况下通过读写锁sync.RWMutex实现对map的并发访问控制,将map和sync.RWMutex封装一下,可以实现对map的安全并发访问,示例代码如下:
package main
import "sync"
type SafeMap struct {
sync.RWMutex
Map map[int]int
}
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()
}
func main() {
safeMap := newSafeMap(10)
for i := 0; i < 100000; i++ {
go safeMap.writeMap(i, i)
go safeMap.readMap(i)
}
}