Reader 与 Writer
我们新设计的TicketStore
能够运行,但其读取性能并不理想:同一时间只有一个客户端能读取特定的票证,因为Mutex<T>
并未区分读者和写者。
我们可以通过使用另一种锁原语RwLock<T>
来解决这个问题。RwLock<T>
代表读写锁,它允许多个读者同时访问数据,但同一时间只允许一个写者进行操作。
RwLock<T>
提供了两种获取锁的方法:read
和write
。read
返回一个允许你读取数据的守卫,而write
则返回一个允许你修改数据的守卫。
#![allow(unused)] fn main() { use std::sync::RwLock; // 由读写锁保护的整数 let lock = RwLock::new(0); // 获取读锁 let guard1 = lock.read().unwrap(); // 在第一个读锁仍激活的情况下 // 获取**第二个**读锁 let guard2 = lock.read().unwrap(); }
权衡取舍
表面上看,RwLock<T>
似乎是不二之选:它提供了Mutex<T>
功能的超集。如果可以使用RwLock<T>
,为什么还要用Mutex<T>
呢?
有两个关键原因:
- 锁定
RwLock<T>
比锁定Mutex<T>
成本更高。这是因为RwLock<T>
需要追踪活跃读者和写者的数量,而Mutex<T>
只需记录锁是否被持有。如果读取操作远多于写入操作,这种性能开销不是问题,但如果工作负载偏向写入,Mutex<T>
可能是更好的选择。 RwLock<T>
可能导致写者饥饿。如果有持续的读者等待获取锁,写者可能永远没有机会执行。RwLock<T>
不对读者和写者获得锁的顺序提供任何保证,这取决于底层操作系统的实现策略,对写者可能不公平。
在我们的案例中,预期的工作负载偏向读取(因为大多数客户端将读取票证而非修改),因此RwLock<T>
是一个合适的选择。