go中实现并发安全的ConcurrentMap

之前在项目中需要在内存中用map维护诸多设备结构信息(包含设备名,设备状态,控制信道长连接,以及长短数据信道连接,资源信息指针等)。我们知道go实现的map不是多协程安全的(并发访问可能导致):

fatal error:concurrent map read and map write 

为了避免这个问题,在map结构中添加了读写锁来保护临界区,如下:

type RWDevMap struct {
	sync.RWMutex
	DevMap map[string]*Dev
}

开始因为还是开发阶段并没有出现什么问题,但是后面进行压力测试时(模拟大规模设备同时上线,下线),由于设备上下线会导致设备状态和管理的连接的字段修改,涉及到并发访问设备map的问题,因为是加锁访问,这就成了串行访问了。当然如果只是用于管理设备,似乎不会有什么问题——至多设备上线延迟一会罢了,这点和网络延迟比起来似乎也可以理解。但是因为当时的设计中,有一个取得代理连接过程,前端发出代理请求后,需要通过map查询到对应设备主动的连接,作为一个数据通道,大概框架如下:

go中实现并发安全的ConcurrentMap_第1张图片

当模拟大规模设备上下线时,前端请求代理连接等设计到查询map的操作就会出现异常卡顿。当时就考虑到与设备竞争map的问题。在接触java的时候,了解jdk中有一个ConcurrentHashMap的东西,于是也想搞一个。但是当时没有深入去了解,只是大概了解了是通过分段加锁的方式实现的。当时时间还是比较紧,就上github看看有没有类似的方案,果然男性交友社区从来都不会让人失望的——https://github.com/streamrail/concurrent-map

虽然当时当时引入ConcurrentMap并没有完美解决大规模设备上下线导致竞争map的问题,但是已经改善很多了(初略估计可以至多提升32倍,哈哈)。看了下实现,比较简单,关键的地方就是分段:

var SHARD_COUNT = 32

// A "thread" safe map of type string:Anything.
// To avoid lock bottlenecks this map is dived to several (SHARD_COUNT) map shards.
type ConcurrentMap []*ConcurrentMapShared

// A "thread" safe string to anything map.
type ConcurrentMapShared struct {
	items        map[string]interface{}
	sync.RWMutex // Read Write mutex, guards access to internal map.
}

// Creates a new concurrent map.
func New() ConcurrentMap {
	m := make(ConcurrentMap, SHARD_COUNT)
	for i := 0; i < SHARD_COUNT; i++ {
		m[i] = &ConcurrentMapShared{items: make(map[string]interface{})}
	}
	return m
}

可以看到ConcurrentMap 封装了32个带读写锁的go原生map。那如何插入/查询数据呢?或数据如何分配到这32个map呢?

首先会根据key计算数据分配到那个map:

// Returns shard under given key
func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared {
	return m[uint(fnv32(key))%uint(SHARD_COUNT)]
}

然后在加锁保护待操作的map,以插入为例:

// Sets the given value under the specified key.
func (m *ConcurrentMap) Set(key string, value interface{}) {
	// Get map shard.
	shard := m.GetShard(key)
	shard.Lock()
	shard.items[key] = value
	shard.Unlock()
}
因为有32张map,除非访问到同一张map,出现资源竞争,访问其他map都可以并发的访问。这样就有32张map可以同时操作,也就是有32倍的效率提升。如何?当然可以加大map的数量,到底多大合适,这个我没有具体研究,有兴趣的可以去试试,欢迎讨论


你可能感兴趣的:(Golang)