目的

Reader-Writer自旋锁允许同一数据结构上的多个读取器或一个单一写入器。

如果受保护的结构经常被读取而很少被写入,则这很有用。

例如,Linux任务列表受rwlock保护。

多个进程可能正在扫描任务列表,或者单个进程可能会修改任务列表。

实现

rwlocks for Itanium的实现也使用单个32位值。

每个阅读器的锁定值都会增加。

如果锁定值是正数,则锁定值将计算活动读取器的数量。

如果写入器处于活动状态,则设置位31,并且32位带符号的值为负。

如果锁值为零,则锁未使用。

可以像常规的自旋锁锁定和解锁操作一样实现对编写器的锁定操作。

读写锁

但是,将0x8000写入计数器而不是1设置位31。

写解锁只是通过使用CMPXCHG将该位复位。

它不能向锁定值写入零,因为读取器可能会对锁定值进行一些递增和递减操作(即使在写入器具有31set的情况下),并且写入器不应打扰读取器计数。

CMPXCHG向锁添加了另一个原子信号量操作。

这意味着写锁的性能比常规自旋锁差,因为执行了两次CMPXCHG操作而不是执行一次。

读取器递增锁定值,然后检查锁定值是否为负。

如果不是负数,则表示已成功获取读锁。

解锁只是锁值的递减。

但是,增量和减量都是昂贵的信号量操作,即使在无竞争的情况下,也使读取器锁比常规自旋锁更昂贵。

如果增量后锁的值为负,则第31位被置位,我们知道写程序正在持有该锁。

读取器通过递减锁定值来取消递增,然后等待直到计数器大于零。

这意味着读取器对争用失败的锁使用两个原子的信号量操作,这为高速缓存行反弹提供了更多的机会。

通常,读取器写入器锁的性能行为将比常规自旋锁的性能差

小结

读写锁的有点是降低了锁的粒度,将读和写分开处理。

读读之间不互斥,这样性能比较好。

但是相对的,失败的时候需要 2 个原子的信号量操作,有时候性能反而会降低。

参考资料

linux 锁实现