1、介绍
在sync.Mutex基础上实现的读写锁,支持读读并发,其他还是串行,适用于读多写少的场景
2、结构体
type RWMutex struct {
    w           Mutex        // held if there are pending writers
    writerSem   uint32       // semaphore for writers to wait for completing readers
    readerSem   uint32       // semaphore for readers to wait for completing writers
    readerCount atomic.Int32 // number of pending readers
    readerWait  atomic.Int32 // number of departing readers
}
        - 
w :一个
Mutex,仅用于解决多个writer之间的竞争问题, - 
writerSem:一个信号量,用于阻塞writer等待正在进行的reader完成。就是用于写操作加锁时候要等待所有的读操作完成,当所有读操作解锁结束后,会发送信号,然后写操作被唤醒
 - 
readerSem:一个信号量,用于阻塞reader等待正在进行的writer完成。读操作要获取锁时候,需要等待写操作解锁,解锁后向该信号量发送信号,解锁所有读操作
 - 
readerCount:记录当前正在进行的reader的数量,也用于表示是否有writer正在等待。
 - 
readerWait :记录writer请求锁时需要等待完成的reader的数量。待完成的读操作计数器,仅有写操作等待时候有用,代表在写操作前已经在执行的读操作数量
 
核心流程
3.1 读加锁
            
            
              Go
              
              
            
          
          func (rw *RWMutex) RLock() {
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	if rw.readerCount.Add(1) < 0 {
		// A writer is pending, wait for it.
		runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)
	}
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
	}
}
        原子操作 readCount 直接加1,如果结果不小于0,则说明加锁成功
如果小于0,则说明当前有writer正在等待或已经持有锁,则当前goroutine会阻塞在readerSem上,直到没有writer持有锁
注意: readCount小于0的原因是有写操作加锁时候会readCount-2的30次方,导致其值小于0
3.2 读解锁
func (rw *RWMutex) RUnlock() {
    if race.Enabled {
       _ = rw.w.state
       race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
       race.Disable()
    }
    if r := rw.readerCount.Add(-1); r < 0 {
       // Outlined slow-path to allow the fast-path to be inlined
       rw.rUnlockSlow(r)
    }
    if race.Enabled {
       race.Enable()
    }
}
        func (rw *RWMutex) rUnlockSlow(r int32) {
    if r+1 == 0 || r+1 == -rwmutexMaxReaders {
       race.Enable()
       fatal("sync: RUnlock of unlocked RWMutex")
    }
    // A writer is pending.
    if rw.readerWait.Add(-1) == 0 {
       // The last reader unblocks the writer.
       runtime_Semrelease(&rw.writerSem, false, 1)
    }
}
        原子操作 readCount 直接减1,如果结果不小于0,说明无写协程等待,直接解锁完成
如果小于0,说明有写协程正在等待,进入慢流程:
原子操作把readerWait减1,若等于0 说明所有读操作都执行结束了,向writerSem发送信号,去唤醒等待的写协程。
大于0,说明还有其他读操作,则不发送信号量直接返回,解锁结束

3.3 写加锁
保证写操作互斥,等待所有的读操作结束
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 := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders
    // Wait for active readers.
    if r != 0 && rw.readerWait.Add(r) != 0 {
       runtime_SemacquireRWMutex(&rw.writerSem, false, 0)
    }
    if race.Enabled {
       race.Enable()
       race.Acquire(unsafe.Pointer(&rw.readerSem))
       race.Acquire(unsafe.Pointer(&rw.writerSem))
    }
}
        先获取mutex的lock锁,确保同时只有一个写操作能进入后续
获取到互斥锁后,将readerCount设置为一个负数(通常是-readerCount-1),表示有writer正在等待锁。如果有正在进行的reader,writer会阻塞在writerSem上,直到所有reader都释放了锁。
r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders 
        精妙之处在于,先把readCount变为负数(使得后面的再进来的读操作进入阻塞),然后又加回来赋值给临时变量r,走到后面判断,如果有读操作还在运行,把readWait设置为原readCount的值,如果大于0,则写操作进入阻塞太,等待所有读操作完成
if r != 0
- 如果 
r == 0,说明没有活跃读者,writer 可以直接获得锁,无需等待。 - 如果 
r > 0,说明有r个读者正在读,需要等他们退出。 
b. rw.readerWait.Add(r) (如果有读操作在运行,则肯定大于0)
- 将 
readerWait设置为r(因为初始为 0,Add(r)后就是r); readerWait表示:"我这个 writer 需要等r个读者退出"。
⚠️ 注意:这里
Add(r)的返回值是更新后的值 。由于初始为 0,返回值就是r,所以!= 0恒成立(只要r != 0)。
c. runtime_SemacquireRWMutex(&rw.writerSem, false, 0)
- 调用底层 runtime 函数,在 
writerSem信号量上阻塞当前 goroutine; - 会一直 sleep,直到有读者在 
RUnlock()时发现readerWait == 0,然后runtime_Semrelease唤醒它。 
✅ 此时,writer 被挂起,等待所有"已进入"的读者退出。
3.4 写解锁
func (rw *RWMutex) Unlock() {
    if race.Enabled {
       _ = rw.w.state
       race.Release(unsafe.Pointer(&rw.readerSem))
       race.Disable()
    }
    // Announce to readers there is no active writer.
    r := rw.readerCount.Add(rwmutexMaxReaders)
    if r >= rwmutexMaxReaders {
       race.Enable()
       fatal("sync: Unlock of unlocked RWMutex")
    }
    // Unblock blocked readers, if any.
    for i := 0; i < int(r); i++ {
       runtime_Semrelease(&rw.readerSem, false, 0)
    }
    // Allow other writers to proceed.
    rw.w.Unlock()
    if race.Enabled {
       race.Enable()
    }
}
        释放写锁。将readerCount恢复为正数(通过加上一个常数,通常是rwmutexMaxReaders 2的30次方),表示writer已经释放了锁,此时如果有等待的reader或writer,它们可以根据情况被唤醒。
如果r大于0,代表有多个r还在等待,则通过runtime_Semrelease(&rw.readerSem, false, 0) 发送解锁信号量,唤醒读操作
等于0 则说明 没有读操作在等下,继续往下,
执行mutex.Unlock互斥锁解锁,写操作解锁完成,解锁过程中通知其他写操作被唤醒