美文网首页Golang随笔-生活工作点滴
Go 并发实战 -- sync RWMutex

Go 并发实战 -- sync RWMutex

作者: 邹志全 | 来源:发表于2019-07-10 22:52 被阅读10次

前言

sync中包含Mutex、RWMutex两个排他锁,上一篇介绍了最基础的Mutex锁,本篇来说一下基于Mutex实现的RWMutex,RWMutex是一把读写锁,功能上跟Java中的读写锁比较相近,适用于多读少写的场景,而Mutex适用于读写次数不确定的场景。下面来看一下RWMutex的使用及实现。

语法基础

RWMutex 函数相对于Mutex来说稍微多一些:


image.png

Lock() : 写锁加锁
UnLock():写锁释放锁
RLock():读锁加锁
RUnLock():读锁释放锁

func Read(lock sync.RWMutex) {
    go func() {
        lock.RLock()
        // read
        lock.RUnlock()
    }()
}

func Write(lock sync.RWMutex) {
    go func() {
        lock.Lock()
        // write
        lock.Unlock()
    }()
}

读锁可以被获取多次,而写锁仅能被获取一次,读锁很像一个引用计数器,写锁其实就是个Mutex全局锁。
这里有个问题:go中的这些锁都是不可重入的,这一点跟Java差异非常大,因为go作者认为需要可重入本身就是代码写的有问题。
除了重入这事儿,但凡涉及读写锁肯定就有锁升级锁降级这些问题。go中的锁不能重入,也不允许获取读锁后再拿写锁(Java会导致死锁,而go是直接不允许),拿到写锁再拿读锁也是不可以的。
可以写几个简单的demo测试一下:

// fatal error: all goroutines are asleep - deadlock!
func TestLockUp(lock sync.RWMutex) {
    lock.RLock()
    fmt.Println("read lock sucess")
    lock.Lock()
    fmt.Println("write lock success")
    lock.Unlock()
    lock.RUnlock()
}

// fatal error: all goroutines are asleep - deadlock!
func TestLockDown(lock sync.RWMutex) {
    lock.Lock()
    fmt.Println("write lock success")
    lock.RUnlock()
    fmt.Println("read lock sucess")
    lock.RUnlock()
    lock.Unlock()
}

// 相当于加了两遍读锁
func TestReRLock(lock sync.RWMutex) {
    lock.RLock()
    fmt.Println("read lock 1 success")
    lock.RLock()
    fmt.Println("read lock 2 sucess")
    lock.RUnlock()
    lock.RUnlock()
}
// fatal error: all goroutines are asleep - deadlock!
func TestReLock(lock sync.RWMutex) {
    lock.Lock()
    fmt.Println("write lock 1 success")
    lock.Lock()
    fmt.Println("write lock 2 sucess")
    lock.RUnlock()
    lock.RUnlock()
}

为了避免一直有读锁占锁,写锁加不上导致的饥饿问题,go设计为写锁优先级较高,有写锁等待时,优先加写锁,具体实现是这么做的:
如果在添加写锁之前已经有其他的读锁和写锁,则lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的 Lock 调用会从获得的锁中排除新的读取器(已有的读锁加了也就加了,有写锁请求后会优先加写锁,读锁先等等)

关于RWMutex的实现,比较简单并且源码中的注释也非常详尽,核心的加锁逻辑主要是Mutex中实现的,这里就不过多赘述了,大家看源码就好了src/sync/rwmutex,关于RWMutex暂时先介绍这么多。

相关文章

  • Go 并发实战 -- sync RWMutex

    前言 sync中包含Mutex、RWMutex两个排他锁,上一篇介绍了最基础的Mutex锁,本篇来说一下基于Mut...

  • Go超时锁的设计和实现

    Go提供两种锁:sync.Mutex和sync.RWMutex。 sync.Mutex: 互斥锁。任意时刻,只能有...

  • Go - sync.RWMutex

    设计目的 大多数读请求之间互不影响,在读多写少的场景下,可以分离读写操作,提高读写并发性能. 限制 只能读读并发,...

  • Golang学习笔记之互斥锁(Mutex)

    Go语言包中的sync包提供了两种锁,互斥锁(sync.Mutex)和读写锁(sync.RWMutex) 这一篇博...

  • GO sync.RWMutex - 解决并发读写问题

    当多个线程访问共享数据时,会出现并发读写问题(reader-writer problems)。有两种访问数据的线程...

  • Go 并发实战 -- sync Once

    前言 Once是一个非常实用的API,它保证了一个事情仅做一次,这个在许多场景非常有用,所以Once也是go提供的...

  • Go 并发实战 -- sync WaitGroup

    前言 waitgroup也是一个非常有用的并发工具,有点像是Java中的CyclicBarrier,只不过Go中的...

  • Go 并发实战 -- sync Cond

    前言 go中的sync.Cond也就是condition,是一个条件同步变量,与Java中Object的wait、...

  • Go 并发实战 -- sync Mutex

    前言 在并发编程中我们可以使用channel来协同各个goroutine,但是很多场景我们也是需要使用sync的比...

  • Go 并发实战 -- sync Pool

    前言 sync包中最后一个要说的API是sync.Pool,本质上来说Pool应该不算是sync并发相关的内容,但...

网友评论

    本文标题:Go 并发实战 -- sync RWMutex

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