Go | 如何实现并发安全的map?
Go | 如何实现并发安全的map?
天行健,君子以自强不息
众所周知,Go语言的标准库中的map类型是非并发安全的。这意味着如果多个goroutine同时访问一个map,并且至少有一个goroutine在写操作,那么必须通过外部同步来避免竞态条件(race condition)。Go的官方文档也明确指出了这一点。
为避免并发问题,主要有两种解决方案,一是使用互斥锁进行同步访问,二是使用标准库中的并发安全的sync.Map,本文主要介绍第一种,即如何使用互斥锁封装并发安全的map,已将代码开源,可直接移步至:github.com/byteweap/ma…
为了封装的map更加简单、方便使用,封装的map同时支持并发安全与非安全以及范型支持。首先定义了Mapx接口,包括了Get()、Set()等函数,mapx.go代码如下:
// Mapx接口
type Mapx[Key comparable, Value any] interface {
Get(Key) (Value, bool) // 获取值
Set(Key, Value) // 设置key-value
Delete(Key) // 删除key
Len() int // map长度
Range(fn func(Key, Value)) // 遍历
Keys() []Key // 所有key
}
// 新建Mapx
// Key为可比较类型,Value为任意类型
// threadSafe: true时,并发安全,false时,并发不安全
func New[Key comparable, Value any](threadSafe bool) Mapx[Key, Value] {
if threadSafe {
return newSafeMapx[Key, Value]() // 线程安全mapx
}
return newUnSafeMapx[Key, Value]() // 非线程安全mapx
}
定义好了Mapx接口,需要对接口中所有函数进行实现,分为两个文件: safemapx.go 并发安全实现, unsafemapx.go 非并发安全实现。
package mapx
import "sync"
// 并发安全的实现,实现了Mapx中所有函数
type safeMapx[Key comparable, Value any] struct {
mux sync.RWMutex // 互斥锁
m map[Key]Value // map数据
}
// 新建
func newSafeMapx[Key comparable, Value any]() *safeMapx[Key, Value] {
return &safeMapx[Key, Value]{m: make(map[Key]Value)}
}
// 获取key值
func (m *safeMapx[Key, Value]) Get(key Key) (Value, bool) {
m.mux.RLock()
defer m.mux.RUnlock()
val, ok := m.m[key]
return val, ok
}
// 设置key-value
func (m *safeMapx[Key, Value]) Set(key Key, value Value) {
m.mux.Lock()
defer m.mux.Unlock()
m.m[key] = value
}
// 删除key
func (m *safeMapx[Key, Value]) Delete(key Key) {
m.mux.Lock()
defer m.mux.Unlock()
delete(m.m, key)
}
// map长度
func (m *safeMapx[Key, Value]) Len() int {
m.mux.RLock()
defer m.mux.RUnlock()
return len(m.m)
}
// 遍历,此处取出map数据后已解锁,所以不存在Range过程中重复上锁造成的死锁问题
func (m *safeMapx[Key, Value]) Range(fn func(Key, Value)) {
m.mux.RLock()
data := m.m
m.mux.RUnlock()
for k, v := range data {
fn(k, v)
}
}
// 所有key
func (m *safeMapx[Key, Value]) Keys() []Key {
m.mux.RLock()
defer m.mux.RUnlock()
keys := make([]Key, 0, len(m.m))
for k := range m.m {
keys = append(keys, k)
}
return keys
}
package mapx
// 非并发安全的实现即原生map,实现了Mapx中所有函数,
type unsafeMapx[Key comparable, Value any] map[Key]Value
// 新建
func newUnSafeMapx[Key comparable, Value any]() unsafeMapx[Key, Value] {
return make(unsafeMapx[Key, Value])
}
// 获取key值
func (m unsafeMapx[Key, Value]) Get(key Key) (Value, bool) {
val, ok := m[key]
return val, ok
}
// 设置key-value
func (m unsafeMapx[Key, Value]) Set(key Key, value Value) {
m[key] = value
}
// 删除key
func (m unsafeMapx[Key, Value]) Delete(key Key) {
delete(m, key)
}
// map长度
func (m unsafeMapx[Key, Value]) Len() int {
return len(m)
}
// 遍历,此处取出map数据后已解锁,所以不存在Range过程中重复上锁造成的死锁问题
func (m unsafeMapx[Key, Value]) Range(fn func(Key, Value)) {
for k, v := range m {
fn(k, v)
}
}
// 所有key
func (m unsafeMapx[Key, Value]) Keys() []Key {
keys := make([]Key, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
如有发现问题,评论区中指出,欢迎大家一起探讨!!!如对你有帮助,麻烦点个小赞赞!!
转载自:https://juejin.cn/post/7374595069746413594