likes
comments
collection
share

Go | 如何实现并发安全的map?

作者站长头像
站长
· 阅读数 77

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 非并发安全实现。

safemapx.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
}

unsafemapx.go

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
评论
请登录