golang 中实现并发安全的map
编辑于 2022-10-12 16:26:26 阅读 905
Go语言中的 map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。
问题还原
下面来看下并发情况下读写 map 时会出现的问题
func main() {
m := make(map[int]int)
go func() {
// 不停地对map进行写入
for {
m[1] = 1
}
}()
go func() {
// 不停地对map进行读取
for {
_ = m[1]
}
}()
}
会报错
fatal error: concurrent map read and map write
实现方案1 - 加锁
// 加锁的map
type Map struct {
m map[int]int
sync.RWMutex
}
func (this *Map) Get(key int) int {
this.RLock()
defer this.RUnlock()
v := this.m[key]
return v
}
func (this *Map) Set(k, v int) {
this.Lock()
defer this.Unlock()
this.m[k] = v
}
func main() {
newMap := &Map{m: make(map[int]int)}
for i := 0; i < 1000; i++ {
go newMap.Set(i, i)
}
for i := 0; i < 1000; i++ {
go fmt.Println(i, newMap.Get(i))
}
time.Sleep(time.Second)
}
实现方案2 - sync.Map
type Map struct
func (m *Map) Store(key, value interface{})
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
func (m *Map) Range(f func(key, value interface{}) bool)
func (m *Map) Delete(key interface{})
sync.Map 有以下特性:
- 无须初始化,直接声明即可。
- sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用,Store 表示存储,Load 表示获取,Delete 表示删除。
- 使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在需要继续迭代遍历时,返回 true,终止迭代遍历时,返回 false。
- LoadOrStore:参数是一对key:value,如果该key存在且没有被标记删除则返回原先的value(不更新)和true;不存在则store,返回该value 和false
func main() {
var m sync.Map
m.Store("bb", 22)
m.Store("cc", 33)
m.Store("aa", 11)
m.Store("dd", 33)
m.Store("ee", 11)
//Load 方法,获得value
if v, ok := m.Load("cc"); ok {
fmt.Printf("Load 方法,获得value %v: %v\n", v, ok)
}
m.Delete("cc")
//LoadOrStore方法,获取或者保存
//就是如果key还在,那么就保持原来并返回原来的值,如果key不存在就存储
if vv, ok := m.LoadOrStore("bb", 22); ok {
fmt.Println(vv)
} else {
fmt.Printf("LoadOrStore 方法,获得value %v: %v\n", vv, ok)
}
//遍历该map
m.Range(func(key, value interface{}) bool {
fmt.Printf("[%v]=[%v]\n", key, value)
return true
})
}
实现方案3 - 分段锁
未完,待续。。。
参考
https://blog.csdn.net/zhizhengguan/article/details/107879969
https://blog.csdn.net/lanyang123456/article/details/114178724