前言:
记得在sync.map出来之前,都是自己用map和rwlock读写锁实现的并发map。 后来,Golang官方在 1.9 加入了协程安全的 sync.map , 性能和安全得以保证,就是没有Length、Size 方法. 至于官方为什么不加Length方法原因,有兴趣的可以看看issue. 简单说官方认为 map这个东东本就不应该有length的实现… 真霸道…
如果只是想看sync.map大小的话,是否可以直接通过store和delete做外部的计数器? 不行,因为delete 没有返回值,也就是说,你无法判断你删除的key是否在map中,是否删除? 如果先get,然后再去delete,组合性的命令又不能保证原子,并发下计数肯定是有问题。。。
为了避免这样那样的问题,直接索性自己改sync.map的代码,加入map大小和delete返回值的问题。 代码改动不超过50行,在现有的sync.map里加个atomic计数,sync.map维护了read和dirty两组数据,数据转换之间也是有锁的,如果为了统计map的大小再加锁,那就得不偿失了。。。 所以说,尽量在sync.map已有的锁里加计数逻辑, 不要加锁。 如果你只是想简单的计数,那么可以直接用sync.map的迭代器,把map的k v都迭代出来做计数,这居然是stackoverflow里投票最高的答案。
该文章后续会有更新, 原文地址 http://xiaorui.cc/?p=4972
代码已经扔到github里面,有兴趣的朋友可以看看。 https://github.com/rfyiamcool/syncmap
sync.map的存入方法.
// xiaorui.cc
func (m *Map) Store(key, value interface{}) {
read, _ := m.read.Load().(readOnly)
if e, ok := read.m[key]; ok && e.tryStore(&value) {
return
}
m.mu.Lock()
if m.counter == nil {
v := new(int64)
*v = 0
m.counter = v
}
read, _ = m.read.Load().(readOnly)
if e, ok := read.m[key]; ok {
if e.unexpungeLocked() {
// The entry was previously expunged, which implies that there is a
// non-nil dirty map and this entry is not in it.
m.dirty[key] = e
}
e.storeLocked(&value)
} else if e, ok := m.dirty[key]; ok {
e.storeLocked(&value)
} else {
atomic.AddInt64(m.counter, 1)
if !read.amended {
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
m.read.Store(readOnly{m: read.m, amended: true})
}
m.dirty[key] = newEntry(value)
}
m.mu.Unlock()
}
Delete 和 Length 函数
// xiaorui.cc
func (m *Map) Delete(key interface{}) (res bool) {
read, _ := m.read.Load().(readOnly)
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
read, _ = m.read.Load().(readOnly)
e, ok = read.m[key]
if !ok && read.amended {
delete(m.dirty, key)
atomic.AddInt64(m.counter, -1)
res = true
}
m.mu.Unlock()
}
if ok {
e.delete()
res = true
atomic.AddInt64(m.counter, -1)
}
return res
}
func (m *Map) Length() *int64 {
return m.counter
}
经过我的测试这个length和delete是没问题的。这里不需要替换标准库,自己import一个自定义包就可以了。 但话说回来,自己去改golang标准库还是很蛋疼的。现在go1.9是可以用,后期golang更新了sync.map特性,难道我还要跟进? 真心希望官方把这几个功能做了。
END>
