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并不是线程安全的。

  • 相关阅读:
    How to tell your iPhone application that location services are required | The Agile Warrior
    How to detect whether socket is still connected...
    Ubuntu Touch On Nexus4 Manual Install (手动安装) under Gentoo
    LanguageTag
    » Working Around JNI UTF-8 Strings Deprogramming
    Mget is available.
    Fix Valgrind's must-be-redirected error in Gentoo
    vector 测试
    abc
    Effective STL 43: Prefer algorithm calls to hand-written loops
  • 原文地址:https://www.cnblogs.com/double12gzh/p/13608579.html
Copyright © 2011-2022 走看看