zoukankan      html  css  js  c++  java
  • 在GoLang中实现线程安全的字典

    1. 背景

    本文主要解释如何通过RWMutex来实现一个基于内存的字典数据结构。

    在项目中,经常需要与并发打交道,这其中很难避免会遇到多个并发的用户同时获取内存中数据的情况,因此我们必须能够有一种方式,可以对这些数据进行读写并发控制。

    2. 实现

    2.1 数据结构定义

    为了达到我们的需求,我设计了以下的自定义的数据结构

    package dictionary
    
    import "sync"
    
    type iKey interface{}
    type iValue interface{}
    
    type Dictionary struct {
        items map[iKey]iValue
        lock sync.RWMutex
    }
    

    对于上面的结构作如下说明:

    • items用于保存所有的key-value数据
    • lock用于控制用户对items的读写操作

    对于RWMutex作如下说明:
    Lock():每次只允许有一个goroutine在同一时刻获取读写
    RLock(): 同一时刻可以有多个goroutine获取

    2.2 方法实现

    接下来,我们实现一下这个Dictionary中所支持一些操作。

    2.2.1 添加key-value

    // Add 向dictionary中添加一个新的key/value
    func (d *Dictionary) Add(key iKey, value iValue) {
    	d.lock.Lock()
    	defer d.lock.Unlock()
    	
    	if d.items == nil {
    		d.items = make(map[iKey]iValue)
    	}
    	
    	d.items[key] = value
    }
    

    说明:

    • 在向dictionary中添加新的key/value前,先通过d.lock.Lock()获取到一个锁,这样可以有效避免一些误操作。当插入成功后,通过d.lock.Unlock()把刚才获取到锁释放掉。
    • 如果一个请求已经获取到了Lock()锁,那么另外一个想要获取RLock()的请求将不得不等待第一个请求释放锁(Unlock())

    2.2.2 删除key-value

    func (d *Dictionary) Remove(key iKey) bool{
    	d.lock.Lock()
    	defer d.lock.Unlock()
    	
    	if _, ok := d.items[key]; ok {
    		delete(d.items, key)
    	}
    	
    	return true
    }
    

    思路同Add这里不再多讲。
    删除操作可以看成是一个操作,因此,同样需要使用Lock()Unlock()

    2.2.3 获取key-value

    func (d *Dictionary) Get(key iKey) iValue {
    	d.lock.RLock()
    	defer d.lock.RUnlock()
    	
    	return d.items[key]
    }
    

    需要强调一点是,如果仅仅是在多个goroutine中并发的去读取数据,那么将不会存在数据竞争的问题。如果我们需要获取Lock()锁,那么必须等到执行完RUnlock()才可以。

    2.2.4 key是否存在

    func (d *Dictionary) Exist(key iKey) bool {
    	d.lock.RLock()
    	defer d.lock.RUnlock()
    	
    	if _, ok := d.items[key]; ok {
    		return true
    	} else {
    		return false
    	}
    }
    

    这个操作我们认为他是一个操作。因此这里使用的是RLock()RUnlock()

    2.2.5 清空所有key-value

    func (d *Dictionary) Clear() {
    	d.lock.Lock()
    	defer d.lock.Unlock()
    	d.items = make(map[iKey]iValue)
    }
    

    这个操作需要获取Lock()Unlock()

    2.2.6 获取字典中元素的个数

    func (d *Dictionary) Size() int {
    	d.lock.RLock()
    	defer d.lock.RUnlock()
    	
    	return len(d.items)
    }
    

    需要先获得读锁

    2.2.7 获取字典中所有的key

    func (d *Dictionary) GetKeys() []iKey {
    	d.lock.RLock()
    	defer d.lock.RUnlock()
    	
    	tempKeys := make([]iKey, 0)
    	for i := range d.items {
    		tempKeys = append(tempKeys, i)
    	}
    	
    	return tempKeys
    }
    

    2.2.8 获取字典中所有的value

    func (d *Dictionary) GetValues() []iValue {
    	d.lock.RLock()
    	defer d.lock.RUnlock()
    	
    	tempValues := make([]iValue, 0)
    	for _, v := range d.items {
    		tempValues = append(tempValues, v)
    	}
    	
    	return tempValues
    }
    

    3. 小结

    GoLang通过内置变量map和包sync.RWMutex提供了一个非常方便的实现字典的方法。map并不是线程安全的。

  • 相关阅读:
    问题-[DelphiXE2]提示第三控件不存在
    问题-[DelphiXE2]编译程序体积大的问题
    问题-[delphi2007、2010]无法二次启动,报EditorLineEnds.ttr被占用,进程一直有bds.exe?
    问题-[VMware Workstation]断电后,重启电脑,之后就提示“内部错误”
    问题-[Delphi]通过Map文件查找内存地址出错代码所在行
    问题-[WIN8.132位系统]安装Win8.1 遇到无法升级.NET Framework 3.5.1
    问题-[DelphiXE7]新建的安桌模拟器运行程序闪退
    问题-[Delphi]用LoadLibrary加载DLL时返回0的错误
    问题-[Access]“无法打开工作组信息文件中的表 'MSysAccounts'”的问题的解决方法
    教程-Delphi 调用控制面板设置功能
  • 原文地址:https://www.cnblogs.com/double12gzh/p/13608579.html
Copyright © 2011-2022 走看看