读写锁
多个协程同时只读
只读时
- 让其他人不能修改即可
- 多协程可以共享读
- 不需要互斥锁
读写锁需求
- 每个锁分为读锁和写锁,写锁互斥
- 没有加写锁时,多个协程都可以加读锁
- 加了写锁时,无法加读锁,读协程排队等待
- 加了读锁, 写锁排队等待
实现读写锁
type RWMutex struct {
w Mutex // 写锁
writerSem uint32 // 作为写协程队列
readerSem uint32 // 作为读协程队列
readerCount int32 // 正值: 正在读的协程 负值:加了写锁
readerWait int32 // 写锁应该等待读协程个数
}
加写锁
- 加写锁, 没有读协程 加互斥锁 w, 才有加写锁的资格, readerCount变为-rwmutexMaxReaders(1 << 30 )
- 加写锁: 有读协程 加互斥锁 w, 才有加写锁的资格, readerCount变为3-rwmutexMaxReaders(1 << 30 ); 表示加了写锁, 但还有三个读协程没有完成, readerWait从3变为0, 才可以加上写锁
总结
- 先加mutex写锁, 若已经被加写锁会阻塞等待
- 将readerCount变为负值, 阻塞读锁的获取
- 计算需要等待多少个读协程释放
- 如果需要等待读协程释放, 陷入writerSem
解写锁
- 将readerCount变为正值, 允许读锁的获取
- 释放在readerSem中等待的读协程
- 解锁mutex
加读锁
- readerCount > 0 没有写锁, 直接加readerCount, 去读即可
- readerCount < 0 有写锁, 给readerCount + 1, 去读队列readerSem排队
总结
- 给readerCount 无脑 + 1
- 如果加完之后readerCount是正数, 加锁成功
- 如果加完之后readerCount是负数, 说明被加了写锁, 陷入readerSem
解读锁
- readerCount > 0 readerCount - 1即可
- readerCount < 0 readerCount - 1, readerWait - 1 如果readerWait = 0, 写锁就可以加上了
总结
- 给readerCount 无脑 - 1
- 如果readerCount是正数, 解锁成功
- 如果readerCount是负数, 有写锁在排队
- 如果是readerWait的最后一个, 唤醒写协程
使用经验
- RW锁适合读多写少的场景, 减少锁冲突
总结
- Mutex用来写协程之间互斥等待
- 读协程使用readerSem等待写锁的释放
- 写协程使用writerSem等待读锁的释放
- readerCount记录读协程个数
- readerWait记录写协程之前的读协程个数