ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

go 并发安全map 分段锁实现

2019-08-05 21:44:05  阅读:692  来源: 互联网

标签:key map ConcurrentMap string mu 并发 func go


一. 简言

1.1 go中的map不是并发安全的

1.2 go1.9版本之前,可以使用map+mutex的方式实现并发安全,但是每次操作,无论读取都要加锁,性能不太好

1.3 go 1.9之后,新增了sync.Map,是并发安全的,效率也很高,具体的源码分析可见笔者的另外一篇博客

        https://blog.csdn.net/yzf279533105/article/details/98108367

1.4 类似java的ConcurrentHashMap的实现,可以对key进行分段,一个段内使用一个锁,这样操作不同的key时,避免锁的阻塞开        销,大大提高效率,这篇博客我们实现的就是go语言的分段锁

二. 代码实现

// 总的map
type ConcurrentMap []*ConcurrentMapShared

// 默认分片数
const SHARE_COUNT int = 64

// 单个map分片
type ConcurrentMapShared struct {
	items map[string]interface{} // 本分片内的map
	mu    sync.RWMutex           // 本分片的专用锁
}

// 新建一个map
func NewConcurrentMap() *ConcurrentMap {
	m := make(ConcurrentMap, SHARE_COUNT)
	for i := 0; i < SHARE_COUNT; i++ {
		m[i] = &ConcurrentMapShared{
			items: map[string]interface{}{},
		}
	}
	return &m
}

// GetSharedMap 获取key对应的map分片
func (m ConcurrentMap) GetSharedMap(key string) *ConcurrentMapShared {
	return m[uint(fnv32(key))%uint(SHARE_COUNT)]
}

// hash函数
func fnv32(key string) uint32 {
	hash := uint32(2166136261)
	prime32 := uint32(16777619)
	for i := 0; i < len(key); i++ {
		hash *= prime32
		hash ^= uint32(key[i])
	}
	return hash
}

// Set 设置key,value
func (m ConcurrentMap) Set(key string, value interface{}) {
	sharedMap := m.GetSharedMap(key) // 找到对应的分片map
	sharedMap.mu.Lock()              // 加锁(全锁定)
	sharedMap.items[key] = value     // 赋值
	sharedMap.mu.Unlock()            // 解锁
}

// Get 获取key对应的value
func (m ConcurrentMap) Get(key string) (value interface{}, ok bool) {
	sharedMap := m.GetSharedMap(key) // 找到对应的分片map
	sharedMap.mu.RLock()             // 加锁(读锁定)
	value, ok = sharedMap.items[key] // 取值
	sharedMap.mu.RUnlock()           // 解锁
	return value, ok
}

// Count 统计key个数
func (m ConcurrentMap) Count() int {
	count := 0
	for i := 0; i < SHARE_COUNT; i++ {
		m[i].mu.RLock() // 加锁(读锁定)
		count += len(m[i].items)
		m[i].mu.RUnlock() // 解锁
	}
	return count
}

// Keys1 所有的key方法1(方法:遍历每个分片map,读取key;缺点:阻塞时间较长)
func (m ConcurrentMap) Keys1() []string {
	count := m.Count()
	keys := make([]string, count)

	// 遍历所有的分片map
	for i := 0; i < SHARE_COUNT; i++ {
		m[i].mu.RLock() // 加锁(读锁定)
		oneMapKeys := make([]string, len(m[i].items))
		for k := range m[i].items {
			oneMapKeys = append(oneMapKeys, k)
		}
		m[i].mu.RUnlock() // 解锁

		// 汇总到keys
		keys = append(keys, oneMapKeys...)
	}

	return keys
}

// Keys2 所有的key方法1(方法:单独起一个协程统计 优点:不阻塞)
func (m ConcurrentMap) Keys2() []string {
	count := m.Count()
	keys := make([]string, count)

	ch := make(chan string, count) // 通道,遍历时
	// 单独起一个协程
	go func() {
		wg := sync.WaitGroup{}
		wg.Add(SHARE_COUNT)

		for i := 0; i < SHARE_COUNT; i++ {
			go func(ms *ConcurrentMapShared) {
				defer wg.Done()

				ms.mu.RLock() // 加锁(读锁定)
				for k := range ms.items {
					ch <- k // 压入通道
				}
				ms.mu.RUnlock() // 解锁
			}(m[i])
		}

		// 等待所有协程执行完毕
		wg.Wait()
		close(ch) // 一定要关闭通道,因为不关闭的话,后面的range不会结束!!!
	}()

	for k := range ch {
		keys = append(keys, k)
	}
	return keys
}

 

标签:key,map,ConcurrentMap,string,mu,并发,func,go
来源: https://blog.csdn.net/yzf279533105/article/details/98502453

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有