zoukankan      html  css  js  c++  java
  • 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的使用

  • 相关阅读:
    R语言中获取当前目录
    Error : .onLoad failed in loadNamespace() for 'rJava', details: call: inDL(x, as.logical(local), as.logical(now), ...) error: 无法载入共享目标对象‘D:/Program Files/R/R-3.2.2/library/rJava/libs/x64/rJava.dll
    Ubuntu打开系统监视器
    Myeclipse中js总是报错
    ubuntu 卸载 google-chrome
    ubuntu下安装myeclipse+破解
    bzoj2085-POI2010-Hamsters
    bzoj1061-[Noi2008]志愿者招募-单纯形 & 费用流
    bzoj2716-天使玩偶
    bzoj3779-重组病毒
  • 原文地址:https://www.cnblogs.com/niuben/p/14817051.html
Copyright © 2011-2022 走看看