Go中的锁(二) | 青训营笔记

79 阅读1分钟

读写锁

多个协程同时只读

只读时

  • 让其他人不能修改即可
  • 多协程可以共享读
  • 不需要互斥锁

读写锁需求

  • 每个锁分为读锁和写锁,写锁互斥
  • 没有加写锁时,多个协程都可以加读锁
  • 加了写锁时,无法加读锁,读协程排队等待
  • 加了读锁, 写锁排队等待

实现读写锁

type RWMutex struct {
	w Mutex // 写锁
	writerSem uint32 // 作为写协程队列
	readerSem uint32 // 作为读协程队列
	readerCount int32 // 正值: 正在读的协程 负值:加了写锁
	readerWait int32 // 写锁应该等待读协程个数
}

Pasted image 20230521175918.png

加写锁

  • 加写锁, 没有读协程 加互斥锁 w, 才有加写锁的资格, readerCount变为-rwmutexMaxReaders(1 << 30 )

Pasted image 20230521180251.png

  • 加写锁: 有读协程 加互斥锁 w, 才有加写锁的资格, readerCount变为3-rwmutexMaxReaders(1 << 30 ); 表示加了写锁, 但还有三个读协程没有完成, readerWait从3变为0, 才可以加上写锁

Pasted image 20230521180720.png

总结

  • 先加mutex写锁, 若已经被加写锁会阻塞等待
  • 将readerCount变为负值, 阻塞读锁的获取
  • 计算需要等待多少个读协程释放
  • 如果需要等待读协程释放, 陷入writerSem

解写锁

Pasted image 20230521181146.png

Pasted image 20230521181322.png

  • 将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记录写协程之前的读协程个数