除了mutex, rwmutex也会经常被使用.

数据结构

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
}

RWMutex中使用 Mutex 来实现writer 排队, 只有一个writer操作. 使用 writerSem 用来 readers通知 正在阻塞的 writer. readerSem 用来 reader/writer Unlock/RUnlock 的时候释放阻塞的reader. readerCount 用来区别 当前是reader还是writer占用锁. readerWait表示writer前面还有等待的reader数量(这样, reader在释放锁的时候, 就可以通过判断 readerWait是否有writer以及是否需要通知writer).

关键方法实现: 1.RLock: 读者计数加1, 没有写的情况下,直接成功返回. 有写入的情况下, 放入 readerSem 的goroutine队列, 挂在semaRoot的平衡树上, 在writer释放锁的时候, 会进行遍历释放 readerSem 上阻塞的goroutine. 其中, writer的判断是通过 readerWait实现的, readerWait 在writer lock的时候会减去最大的reader数量, 这样, 如果reader cas +1小于0, 表示有writer. 2.RUnlock: 注意, RUnlock一个未被加锁的rw, 是抛出异常. 如果是writer 等待了, 就进行唤醒 3.Lock: 每次只有一个writer并发操作, 只有写锁被释放了, 才有其他goroutine获取写锁, 通过mutex来实现. readerCount会减去maxReader, 让其他reader知道有writer. 如果有reader在前面, 就会修改变量 readerWait 通知reader在RUnlock有writer在等待, 并放入writerSem的等待列表. 4.Unlock: readerCount重新加上 rwmutexMaxReaders, 遍历唤醒 readerSem上阻塞的goroutine, 释放内部的mutex.

状态表示: ….

有意思的地方, RWMutex 通过 Mutex来实现 writer 的互斥操作, 但是, Mutex本身就会挂载在 semaRoot的平衡树上, RWMutex的 writerSem 也会挂载在 semaRoot的平衡树上. 感觉多了一个 sema. Mutex 可以 writerSem 公用一个吗?

有趣的实现

RLocker将 RWMutex 转换成 别名, 还重写了方法. 通过返回的是接口, 限制了方法的使用.

// 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 RWMutex

func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }