扩展go sync.map的length和delete方法

前言:

       记得在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>


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc