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版本后提供了一种并发安全的 map
,sync.Map
推荐大家使用。
一.问题
map是线程不安全的,即使并发读写没有冲突也会报错(fatal error: concurrent map read and map write):
-
func main() {
-
m := make(map[int]int)
-
go func() {
-
for {
-
_ = m[1]
-
}
-
}()
-
go func() {
-
for {
-
m[2] = 2
-
}
-
}()
-
time.Sleep(time.Second * 1<em>)
-
}</em>
报错原因:
写的时候设置了h.flags,获取的时候检查报错
写完会清除标志位
二.解决办法
1)使用读写锁sync.RWMutex:
-
var counter = struct{
-
sync.RWMutex
-
m map[string]int
-
}{m: make(map[string]int)}
-
counter.RLock()
-
n := counter.m["some_key"]
-
counter.RUnlock()
-
fmt.Println("some_key:", n)
-
counter.Lock()
-
counter.m["some_key"]++
-
counter.Unlock()
2)使用类似java的Concurrenthashmap(https://github.com/orcaman/concurrent-map)
3)使用sync.Map
-
func main() {
-
m := sync.Map{}
-
m.Store(1, 1)
-
m.Store("1", "1")
-
fmt.Println(m.Load("1"))
-
m.Delete("1")
-
fmt.Println(m.Load("1"))
-
}
补充:对于不容易发现的并发问题,可以使用-race参数进行并发检测
-
func main() {
-
a := 1
-
go func() {
-
a = 2
-
}()
-
a = 3
-
fmt.Println(a)
-
-
time.Sleep(time.Second * 1)
-
}
posted on 2022-08-19 15:00 ExplorerMan 阅读(1028) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2019-08-19 Elasticsearch 安装 Head 插件
2019-08-19 elasticsearch head + xpack 用户名密码访问
2019-08-19 IK分词器的安装与使用IK分词器创建索引
2019-08-19 Mongo-Connector 安装及使用文档
2019-08-19 CentOS7上安装配置破解Elasticsearch+Kibana 6.4.2-6.5.1全过程
2019-08-19 [不错]A step-by-step guide to enabling security, TLS/SSL, and PKI authentication in Elasticsearch
2016-08-19 借助CustomBehaviorsLibrary.dll写出水印效果(转)