go语言协程安全map
Posted wsw-seu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go语言协程安全map相关的知识,希望对你有一定的参考价值。
前言:
在go语言中 map 是很重要的数据结构。Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。问题来了,这么安逸的 数据结构,它不是协程安全的 !当多个 协程同时对一个map 进行 读写时,会抛出致命错误。总结一下 想要 做到 协程安全 map 一共有以下三种方法。
1.map + 锁
这是最常见的一种操作,当要对 map操作的时候就加锁,其他的 协程就等待。下面是代码示例:
package util import "sync" type SafeMap struct { Data map[string]interface{} Lock sync.RWMutex } func (this *SafeMap) Get(k string) interface{} { this.Lock.RLock() defer this.Lock.RUnlock() if v, exit := this.Data[k]; exit { return v } return nil } func (this *SafeMap) Set(k string, v interface{}) { this.Lock.Lock() defer this.Lock.Unlock() if this.Data == nil { this.Data = make(map[string]interface{}) } this.Data[k] = v }
2. sync.map
这个是go 最近版本新推出来的 协程安全 map == 可能是官方也觉得 蛮有必要的吧 。下面的代码 主要写一下使用方法。具体原理我就不介绍了。这里要注意一下 sync.map 不需要 初始化
var test sync.Map //设置元素 func set (k,v interface{}){ test.Store(k,v) } //获得元素 func get (k interface{}) interface{}{ tem ,exit := test.Load(k) if exit { return tem } return nil } //传入一个 函数 ,sync.map 会内部迭代 ,运行这个函数 func ranggfunc (funcs func(key, value interface{}) bool) { test.Range(funcs) } //删除元素 func del(key interface{}){ test.Delete(key) }
3.单协程操作 map ,用 channle 通信
这个思路有点 骚,就是一直由一个协程 操作map ,其他协程 通过 channle 告诉这个协程应该 怎么操作。其实这样子 性能不是很好,因为 channle 底层 也是锁 ,而且 map 存数据 是要 计算hash的 ,之前是 多个协程自己算自己的hash ,现在变成了一个协程计算了。但是这个思路还是可以,不仅仅是 在 map上可以这么操作。socket 通信啊, 全局 唯一对象的调用啊,都可以用此思路。下面给大家看一下我是实现的代码:
package main import ( "fmt" //"time" ) var ( ADD interface{} = 1 DEL interface{} = 2 GET interface{} = 3 ) type safeMap struct { Msq chan *[3] interface{} //[‘type‘,‘id‘,‘value‘,channle] data map[interface{}]interface{} chanl chan interface{} } func NewSafeMap() *safeMap { tem := &safeMap{} tem.init() return tem } func (this *safeMap) init() { this.Msq = make(chan *[3]interface{},10) this.data = make(map[interface{}]interface{}) this.chanl = make(chan interface{},0) go this.run() } func (this *safeMap) run() { for { select { case msg := <- this.Msq : switch msg[0] { case ADD : this.dataAdd(msg[1],msg[2]) case DEL : this.dataDel(msg[1]) case GET : this.dataGet(msg[1]) } } } } func (this *safeMap) msqChan (typ,id,val interface{}) *[3]interface{}{ return &[...]interface{}{typ,id,val} } //保存 或者更新元素 func (this *safeMap) dataAdd (id , value interface{}) { this.data[id] = value } //删除元素 func (this *safeMap) dataDel (id interface{}) { delete(this.data,id) } //获得元素 func (this *safeMap) dataGet (id interface{}) { if val ,exit := this.data[id] ;exit { this.chanl <- val return } this.chanl <- nil } //----------------------------------------------------对外接口-------------------------------- func (this *safeMap) Add (id ,value interface{}) { this.Msq <- this.msqChan(ADD,id,value) } func (this *safeMap) Del (id interface{}) { this.Msq <- this.msqChan(DEL,id ,nil) } func (this *safeMap) Get (id interface{}) interface{} { this.Msq <- this.msqChan(GET,id,nil) res := <- this.chanl return res } //获得 长度 func (this *safeMap) GetLength() uint32{ return uint32(len(this.data)) } func main() { sa := NewSafeMap() // sa.Add(1,1) sa.Add(2,3) fmt.Println(2,sa.Get(2)) sa.Del(2) fmt.Println(2,sa.Get(2)) }
以上是关于go语言协程安全map的主要内容,如果未能解决你的问题,请参考以下文章
今天说说go多协程并发访问map导致的fatal error