前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang语言情怀-第39期 Go 语言设计模式 读写锁

Golang语言情怀-第39期 Go 语言设计模式 读写锁

作者头像
李海彬
发布2021-03-09 10:54:35
5540
发布2021-03-09 10:54:35
举报
文章被收录于专栏:Golang语言社区

调试程序发现一个报错:fatal error: concurrent map writes 是因为多个goroutine对同一个map产出了竞争,解决这个问题的方法有两个,一个是用sync.Map,另一个是加锁。sync.map是go1.9新加的特性,这里暂且先不讨论。而且当前业务场景用读写锁完全可以解决,所以决定使用读写锁。

先温习一遍概念:

1、互斥锁

其中Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁叶叫做全局锁。

func (m *Mutex) Unlock()用于解锁m,如果在使用Unlock()前未加锁,就会引起一个运行错误.已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他goroutine对其解锁。

互斥锁只能锁定一次,当在解锁之前再次进行加锁,便会无法加锁,如果在加锁前解锁,便会报错“panic: sync: unlock of unlocked mutex”

2、读写锁

RWMutex是一个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景.

func (rw *RWMutex) Lock()  写锁,如果在添加写锁之前已经有其他的读锁和写锁,则lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的 Lock 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定 func (rw *RWMutex) Unlock() 写锁解锁,如果没有进行写锁定,则就会引起一个运行时错误

func (rw *RWMutex) RLock() 读锁,当有写锁时,无法加载读锁,当只有读锁或者没有锁时,可以加载读锁,读锁可以加载多个,所以适用于"读多写少"的场景

func (rw *RWMutex) RUnlock() 读锁解锁,RUnlock 撤销单次RLock 调用,它对于其它同时存在的读取器则没有效果。若 rw 并没有为读取而锁定,调用 RUnlock 就会引发一个运行时错误

概括:

  1. 读锁不能阻塞读锁
  2. 读锁需要阻塞写锁,直到所有读锁都释放
  3. 写锁需要阻塞读锁,直到所有写锁都释放
  4. 写锁需要阻塞写锁
代码语言:javascript
复制
type MapWithLock struct {  //把读写锁和资源map封装在一起
    sync.RWMutex
    M map[string]Kline
}

var KlineDataMemory = make(map[string] interface{})
    var InstrLock sync.RWMutex
    var counter = MapWithLock{    // 生成一个带有锁和map的实例,然后就可以使用啦
        InstrLock,
        KlineDataMemory,
    }

    .
    .
    .

    counter.RLock()      //加读锁
        for _, v := range counter.M {
            instrID = v.InstrumentID
            break
        }
    counter.RUnlock()   //解除读锁    加锁和解锁必须成对出现,并且建议放在同一层次的代码块中

在游戏开发过程中,对于新手来说也会遇到,特别是对于Go语言并发安全比较好实现的情况,很多人开发过程中就忽略了这一点。

并发安全map可以自己实现,也可以使用网上的第三方库;下面推荐给大家一个很好用的第三方的库:

代码语言:javascript
复制
github.com/fanliao/go-concurrentMap

实例例子 如下:

代码语言:javascript
复制
type ProxyServer struct {
    Connection *websocket.Conn
    StrMD5     string
    MapSafe    *concurrent.ConcurrentMap
}

数据保存 如下:

代码语言:javascript
复制
// 客户端发消息---> 服务器保存
func (this *ProxyServer) ServerSendDataToClient(ProtocolData map[string]interface{}) {
    if ProtocolData["OpenID"] == nil || ProtocolData["Data"] == nil {
        panic("error")
    }
    strOpenID := ProtocolData["OpenID"].(string)
    strData := ProtocolData["Data"].(map[string]interface{})
    val, err := this.MapSafe.Get(strOpenID + "|User")
    if err != nil {
        fmt.Println("ServerSendDataToClient:", err)
    }
    if val != nil {
        val.(*ProxyServer).PlayerSendMessage(strData)
    }
    return
}

数据获取 如下:

代码语言:javascript
复制
// 客户端发消息---> 服务器保存
func (this *ProxyServer) ClientSendDataToServer(ProtocolData map[string]interface{}) {
    if ProtocolData["ServerID"] == nil || ProtocolData["Data"] == nil {
        panic("error")
    }
    strServerID := ProtocolData["ServerID"].(string)
    strData := ProtocolData["Data"].(map[string]interface{})
    val, _ := this.MapSafe.Get(strServerID + "|Server")
    if val != nil {
        val.(*ProxyServer).PlayerSendMessage(strData)
    }
    return
}

参考资料:

Go语言读写锁

https://blog.csdn.net/jeffrey11223/article/details/79243084

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言情怀 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档