Go

go 读写锁源码分析

go学习记录

Posted by CHuiL on March 27, 2019
mutexLocked = 1 << iota // mutex is locked
mutexWoken
mutexStarving
mutexWaiterShift = iota

go中的读写锁

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 int32  // number of pending readers(读者总数)
   readerWait  int32  // number of departing readers(当写者要进行写是,当前还必须等待多少个读者读完)
}
func (rw *RWMutex) Lock() {
 ...
   // First, resolve competition with other writers.
   //读者要进行读,所以将readerCount减去rwmutexMaxReaders(1<<30),然后计算当前还有
   //多少读者在读,写入到readerWait中,然后求获取写的信号量从而阻塞;
   rw.w.Lock() //这个地方还要加锁,因为写者只能有一个
   // Announce to readers there is a pending writer.
   r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
   // Wait for active readers.
   if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
      runtime_SemacquireMutex(&rw.writerSem, false)
   }
...
}
func (rw *RWMutex) RLock() {
    ....
    //读者尝试获取,readerCount+1
    //如果小于0,说明前面有写者打算写(被减去rwmutexMaxReaders)
    //所以新来的读者会被阻塞,获取读者信号量从而阻塞;
    //大于0就不需要额外操作
   if atomic.AddInt32(&rw.readerCount, 1) < 0 {
      // A writer is pending, wait for it.
      runtime_SemacquireMutex(&rw.readerSem, false)
   }
...
}
func (rw *RWMutex) RUnlock() {
    ....
    //先将读者总数-1,然后判断是否小于0;
    //同样,小于0表示有写者要准备写了,所以需要在进一步判断
    //readerWait是否为0,为0说明之前写申请的时候还在读的读者都读完了,可以写了,释放读的信号,唤醒写瞎线程
    //大于0同样不操作
   if r := atomic.AddInt32(&rw.readerCount, -1); r < 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 {
         // The last reader unblocks the writer.
         runtime_Semrelease(&rw.writerSem, false)
      }
   }
  ....
}
func (rw *RWMutex) Unlock() {
....
    
   // Announce to readers there is no active writer.
   //写锁解锁,将readerCount加回rwmutexMaxReaders,
   //然后判断是否对没上锁的锁解锁
   //由于在写过程中是不会有其他读者的,所以当前有的读者都在阻塞
   //所以直接循环r次,将读的信号量加回来并唤醒读线程
   r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
   if r >= rwmutexMaxReaders {
      race.Enable()
      throw("sync: Unlock of unlocked RWMutex")
   }
   // Unblock blocked readers, if any.
   for i := 0; i < int(r); i++ {
      runtime_Semrelease(&rw.readerSem, false)
   }
   // Allow other writers to proceed.
   rw.w.Unlock()
   ....
}