在GoLang中實現執行緒安全的字典
阿新 • • 發佈:2020-09-03
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
並不是執行緒安全的。