go map fatal error: concurrent map iteration and map write 讀寫鎖與深度拷貝的坑


起因

從幣安實時拉取交易對的數據,這里使用了 map,用於存放每個交易對的最新價格,由於 map 並不是並發安全的所以加了讀寫鎖。

但系統有時候還是會發生 fatal error: concurrent map iteration and map write 錯誤

使用代碼如下:

type BinanceSymbolPrice struct {
	Item map[string]SymbolPrice
	Lock sync.RWMutex
}

func NewSymbolPrice() *BinanceSymbolPrice {
	info := &BinanceSymbolPrice{
		Item: make(map[string]SymbolPrice),
	}

	return info
}

/**
 * 現貨最新價格行情操作
 */
func (bst *BinanceSymbolPrice) GetAllSymbolPriceInfo() map[string]SymbolPrice {
	bst.Lock.RLock()
	defer bst.Lock.RUnlock()

	return bst.Item
}

func (bst *BinanceSymbolPrice) GetSymbolPriceInfo(symbol string) SymbolPrice {
	bst.Lock.RLock()
	defer bst.Lock.RUnlock()

	info, exist := bst.Item[symbol]
	if !exist {
		return SymbolPrice{}
	}

	return info
}

func (bst *BinanceSymbolPrice) AddSymbolPriceItem(symbol string, SymbolPrice SymbolPrice) {
	bst.Lock.Lock()
	defer bst.Lock.Unlock()

	if _, ok := bst.Item[symbol]; ok {
		return
	}
	bst.Item[symbol] = SymbolPrice
}

func (bst *BinanceSymbolPrice) DelSymbolPriceInfo(symbol string) {
	bst.Lock.Lock()
	defer bst.Lock.Unlock()

	if _, ok := bst.Item[symbol]; !ok {
		return
	}
	delete(bst.Item, symbol)
}

// 更新交易對價格
func (bst *BinanceSymbolPrice) ResetSymbolPriceInfo(symbol string, SymbolPrice SymbolPrice) {
	bst.Lock.Lock()
	defer bst.Lock.Unlock()

	bst.Item[symbol] = SymbolPrice
}

分析

經過自己寫測試文件,才找出來問題的原因,問題代碼如下

func (bst *BinanceSymbolPrice) GetAllSymbolPriceInfo() map[string]SymbolPrice {
	bst.Lock.RLock()
	defer bst.Lock.RUnlock()

	return bst.Item
}

這段代碼的含義是取出map中的所有數據,咋一看沒什么問題,可是忽略了 map 是引用類型,這樣實際上傳遞的是地址,還是會對源數據進行訪問,從而造成了 map 讀寫並發。

修改如下:

func (bst *BinanceSymbolPrice) GetAllSymbolPriceInfo() map[string]SymbolPrice {
	bst.Lock.RLock()
	defer bst.Lock.RUnlock()

	item := make(map[string]SymbolPrice)
	for key, value := range bst.Item {
		item[key] = value
	}
	return item
}

新初始化聲明了一個新 map ,把查詢出來的數據重新copy到新的map中,這樣再使用時,訪問到的數據就不再是同一份數據了,從而避免了 map 讀寫並發。

在Go語言1.9版本后提供了一種並發安全的 mapsync.Map 推薦大家使用。

這篇文章解釋了如何使用 go sync.map的使用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM