golang 中实现并发安全的map
 编辑于 2022-10-12 16:26:26 阅读 1329
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
