前言:
记得在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>