sync/rwmutex|sync/rwmutex go读写锁学习

本文结构

背景 源码 思考 问题

背景 最近写go,看到学习一些go源码记录上,另外相关依赖代码这里简单说明一下
读写锁 概念去搜一下就好,可以上多个读锁,一个写锁
上写锁时要等之前的读锁释放
代码前提 【sync/rwmutex|sync/rwmutex go读写锁学习】信号量的获取与释放,看注释就够了,这个是runtime的
// Semacquire waits until *s > 0 and then atomically decrements it. // It is intended as a simple sleep primitive for use by the synchronization // library and should not be used directly. func runtime_Semacquire(s *uint32)// Semrelease atomically increments *s and notifies a waiting goroutine // if one is blocked in Semacquire. // It is intended as a simple wakeup primitive for use by the synchronization // library and should not be used directly. // If handoff is true, pass count directly to the first waiter. func runtime_Semrelease(s *uint32, handoff bool)

源码 变量
wMutex互斥锁 writerSemuint32写锁信号量 readerSemuint32读锁信号量 readerCount int32还未释放读锁的reader数量(偏差2^30,有write就是原有reader数量-2^30) readerWaitint32写锁要等待的reader数量(之前的reader释放了锁,writer才能跑)

源码 下面的源码没有分析锁竞争(非公平锁)相关代码逻辑
// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file.package syncimport ( "internal/race" "sync/atomic" "unsafe" )// There is a modified copy of this file in runtime/rwmutex.go. // If you make any changes here, see if you should make them there.// A RWMutex is a reader/writer mutual exclusion lock. // The lock can be held by an arbitrary number of readers or a single writer. // The zero value for a RWMutex is an unlocked mutex. // // A RWMutex must not be copied after first use. // // If a goroutine holds a RWMutex for reading and another goroutine might // call Lock, no goroutine should expect to be able to acquire a read lock // until the initial read lock is released. In particular, this prohibits // recursive read locking. This is to ensure that the lock eventually becomes // available; a blocked Lock call excludes new readers from acquiring the // lock. type RWMutex struct { wMutex// held if there are pending writers互斥锁 writerSemuint32 // semaphore for writers to wait for completing readers写锁信号量 readerSemuint32 // semaphore for readers to wait for completing writers读锁信号量 readerCount int32// number of pending readers还未释放读锁的reader数量(偏差2^30,有write就是原有reader数量-2^30) readerWaitint32// number of departing readers写锁要等待的reader数量(之前的reader释放了锁,writer才能跑) }const rwmutexMaxReaders = 1 << 30//假定最多2^30个reader// RLock locks rw for reading. // // It should not be used for recursive read locking; a blocked Lock // call excludes new readers from acquiring the lock. See the // documentation on the RWMutex type. func (rw *RWMutex) RLock() { //读锁 if race.Enabled { _ = rw.w.state race.Disable() } if atomic.AddInt32(&rw.readerCount, 1) < 0 { //当前有写锁了 // A writer is pending, wait for it. runtime_Semacquire(&rw.readerSem) //等待reader信号量 } if race.Enabled { race.Enable() race.Acquire(unsafe.Pointer(&rw.readerSem)) } }// RUnlock undoes a single RLock call; // it does not affect other simultaneous readers. // It is a run-time error if rw is not locked for reading // on entry to RUnlock. func (rw *RWMutex) RUnlock() {//释放写锁 if race.Enabled { _ = rw.w.state race.ReleaseMerge(unsafe.Pointer(&rw.writerSem)) race.Disable() } if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { //reader数量-1,如果<0的话 if r+1 == 0 || r+1 == -rwmutexMaxReaders { //如果已经没有读锁的,还去释放(如释放多次) race.Enable() throw("sync: RUnlock of unlocked RWMutex") } // A writer is pending. 下面的情况代表有写锁 if atomic.AddInt32(&rw.readerWait, -1) == 0 { //写锁的reader wait数量-1 // The last reader unblocks the writer. runtime_Semrelease(&rw.writerSem, false)//如果wait数量到0,释放writer信号量 } } if race.Enabled { race.Enable() } }// Lock locks rw for writing. // If the lock is already locked for reading or writing, // Lock blocks until the lock is available. func (rw *RWMutex) Lock() { if race.Enabled { _ = rw.w.state race.Disable() } // First, resolve competition with other writers. rw.w.Lock() //上互斥锁 // Announce to readers there is a pending writer. r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders//readerCount变成负数,代表有写锁,r就是计算前readCount的绝对值(存疑,atomic计算) // Wait for active readers. if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {//当前readCount不为0,并且上一步r计算之后RUnlock的次数和之前readerCount相同(上一步计算后可能有多次RUnlock,readerWait会变成负数) runtime_Semacquire(&rw.writerSem)//等待writer信号量 } if race.Enabled { race.Enable() race.Acquire(unsafe.Pointer(&rw.readerSem)) race.Acquire(unsafe.Pointer(&rw.writerSem)) } }// Unlock unlocks rw for writing. It is a run-time error if rw is // not locked for writing on entry to Unlock. // // As with Mutexes, a locked RWMutex is not associated with a particular // goroutine. One goroutine may RLock (Lock) a RWMutex and then // arrange for another goroutine to RUnlock (Unlock) it. func (rw *RWMutex) Unlock() { if race.Enabled { _ = rw.w.state race.Release(unsafe.Pointer(&rw.readerSem)) race.Release(unsafe.Pointer(&rw.writerSem)) race.Disable() }// Announce to readers there is no active writer. r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)//readerCount + 2^30变成正数 if r >= rwmutexMaxReaders {//释放写锁多次 race.Enable() throw("sync: Unlock of unlocked RWMutex") } // Unblock blocked readers, if any. for i := 0; i < int(r); i++ {//发送多次reader信号量 runtime_Semrelease(&rw.readerSem, false) } // Allow other writers to proceed. rw.w.Unlock() if race.Enabled { race.Enable() } }// RLocker returns a Locker interface that implements // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock. func (rw *RWMutex) RLocker() Locker { return (*rlocker)(rw) }type rlocker RWMutexfunc (r *rlocker) Lock(){ (*RWMutex)(r).RLock() } func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }

思考 如何区分当前是被读锁还是写锁占有 readerCount>0则是读锁,<0则是写锁
整体思路 通过readerCount和readerWait进行计算
对读写锁的获取进行判断,是否等待reader或者writer信号量
对读写锁的释放也进行判断,释放reader以及writer相关信号量
多次调用写锁不会成功的原理 里面有rw.w.lock(),这个是互斥锁,释放写锁的时候释放这个互斥锁
获取写锁里面if的第二个判断逻辑何时生效 sync/rwmutex|sync/rwmutex go读写锁学习
文章图片
if条件第二句什么时候满足 在上面计算r之后,readerCount可能变化了,即再计算r之后,其他线程又执行了Rlock和Runlock,并且整体Runlock次数>Rlock次数,就有可能出现readerWait为负数了
考虑场景
1.计算r,当前readerCount为1,r为1,计算后readerCount为1-2^30 2.Runlock一次,readerCount为-2^30,满足下面条件

sync/rwmutex|sync/rwmutex go读写锁学习
文章图片
image.png
3.此时readerWait设置成-1 4.Lock里面,计算if条件,第一个条件r!=0(满足) 第二个条件readerWait=-1,r=1,相加为0,满足条件,不用处理

问题 代码假定最多有2^30个readers 所以代码里面readerCount在写模式会-2^30
如何和java读写锁比较 印象里面java里面是有AQS队列,公平非公平锁的概念更清晰,并且记录了队列结构

    推荐阅读