go RWMutex源码分析

适用场景

并发场景下读多写少。

字段含义

RWMutex基于Mutex,写优先,Lock函数(反转readerCount)会阻止新的reader获取锁。

type RWMutex struct { 
  w Mutex   // writer之间互斥 
  writerSem  uint32  // writer信号量 
  readerSem  uint32  // reader信号量 
  readerCount int32   // reader数量 
  readerWait int32   // writer等待reader的数量 
} 
// reader最大数量
const rwmutexMaxReaders = 1 << 30

RLock

func (rw *RWMutex) RLock() { 
    // 如果readerCount是负值,那么有writer请求锁时,阻塞后来的reader
    if atomic.AddInt32(&rw.readerCount, 1) < 0 {  
        runtime_SemacquireMutex(&rw.readerSem, false, 0) 
    }
}

RUnlock

func (rw *RWMutex) RUnlock() { 
    if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
        // 有等待的writer
        rw.rUnlockSlow(r) 
    } 
}

func (rw *RWMutex) rUnlockSlow(r int32) {
   // 最后一个reader释放锁后唤醒writer
  if atomic.AddInt32(&rw.readerWait, -1) == 0 {
      runtime_Semrelease(&rw.writerSem, false, 1) 
  } 
}

Lock

func (rw *RWMutex) Lock() {
    rw.w.Lock()
    // 反转readerCount,告诉reader有writer竞争锁,阻塞后面的reader
    // readerCount = readerWait(writer前面的读者数量)+writer后面的读者数量
    r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders 
    // 如果当前有reader持有锁,那么等待前面的reader释放锁
    if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { 
        runtime_SemacquireMutex(&rw.writerSem, false, 0) 
    } 
} 

Unlock

func (rw *RWMutex) Unlock() { 
    // 反转readerCount,新来的reader可以直接获取到读锁
    r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) 

    // 唤醒writer后所有reader
    for i := 0; i < int(r); i++ { 
        runtime_Semrelease(&rw.readerSem, false, 0) 
    }

    rw.w.Unlock() 
}

死锁—加读锁->加写锁->加读锁

package main

import (
   "sync"
   "time"
)

func main() {
   m := sync.RWMutex{}
   m.RLock()

   go func() {
      m.Lock()
   }()
   time.Sleep(1 * time.Second)
   
   m.RLock()
}

Go的读写锁没有记录持有锁的协程信息,不支持重入和降级。
Java的ReentantReadWriteLock支持降级(持有写锁的线程可继续获取到读锁)和可重入。

posted on 2023-01-21 19:30  王景迁  阅读(42)  评论(0编辑  收藏  举报

导航