目的
Reader-Writer自旋锁允许同一数据结构上的多个读取器或一个单一写入器。
如果受保护的结构经常被读取而很少被写入,则这很有用。
例如,Linux任务列表受rwlock保护。
多个进程可能正在扫描任务列表,或者单个进程可能会修改任务列表。
实现
rwlocks for Itanium的实现也使用单个32位值。
每个阅读器的锁定值都会增加。
如果锁定值是正数,则锁定值将计算活动读取器的数量。
如果写入器处于活动状态,则设置位31,并且32位带符号的值为负。
如果锁值为零,则锁未使用。
可以像常规的自旋锁锁定和解锁操作一样实现对编写器的锁定操作。
但是,将0x8000写入计数器而不是1设置位31。
写解锁只是通过使用CMPXCHG将该位复位。
它不能向锁定值写入零,因为读取器可能会对锁定值进行一些递增和递减操作(即使在写入器具有31set的情况下),并且写入器不应打扰读取器计数。
CMPXCHG向锁添加了另一个原子信号量操作。
这意味着写锁的性能比常规自旋锁差,因为执行了两次CMPXCHG操作而不是执行一次。
读取器递增锁定值,然后检查锁定值是否为负。
如果不是负数,则表示已成功获取读锁。
解锁只是锁值的递减。
但是,增量和减量都是昂贵的信号量操作,即使在无竞争的情况下,也使读取器锁比常规自旋锁更昂贵。
如果增量后锁的值为负,则第31位被置位,我们知道写程序正在持有该锁。
读取器通过递减锁定值来取消递增,然后等待直到计数器大于零。
这意味着读取器对争用失败的锁使用两个原子的信号量操作,这为高速缓存行反弹提供了更多的机会。
通常,读取器写入器锁的性能行为将比常规自旋锁的性能差。
小结
读写锁的有点是降低了锁的粒度,将读和写分开处理。
读读之间不互斥,这样性能比较好。
但是相对的,失败的时候需要 2 个原子的信号量操作,有时候性能反而会降低。