美文网首页Go知识库GO
go语言协程安全map

go语言协程安全map

作者: xyt001 | 来源:发表于2018-11-04 18:02 被阅读107次

    前言:

    在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))
    }
    

    后言:

    以上就是我对多协程操作map的所有思路了,欢迎评论一起学习!

    相关文章

      网友评论

        本文标题:go语言协程安全map

        本文链接:https://www.haomeiwen.com/subject/upfdxqtx.html