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

  • 相关阅读:
    0309. Best Time to Buy and Sell Stock with Cooldown (M)
    0621. Task Scheduler (M)
    0106. Construct Binary Tree from Inorder and Postorder Traversal (M)
    0258. Add Digits (E)
    0154. Find Minimum in Rotated Sorted Array II (H)
    0797. All Paths From Source to Target (M)
    0260. Single Number III (M)
    0072. Edit Distance (H)
    0103. Binary Tree Zigzag Level Order Traversal (M)
    0312. Burst Balloons (H)
  • 原文地址:https://www.cnblogs.com/double12gzh/p/13608579.html
Copyright © 2011-2022 走看看