Latch的读写模式
- 读模式:多个线程在同一时间能够读取相同的对象;如果一个线程获持有一个处于读模式的latch,另一个线程也能够获取该处于读模式的latch。
- 写模式:只有一个线程能够获取该对象;如果有其他线程持有处于任意模式下的latch,则其他线程是不能够获得write latch的。
- 相容性矩阵:
Latch的实现
Reader-Writer Locks
- 允许多个并发读者
- 必须管理读写队列来避免饿死
- 可以在自旋锁的基础上加以实现
可以用互斥量和条件变量来实现rwlatch
, 在src/include/common/rwlatch.h
中实现了 ReaderWriterLatch
类。
数据成员和构造析构函数
互斥量 mutex_
来保护当前的读者个数reader_count_
(读者队列上最多有 UINT_MAX
个读者)和是否有写者进入writer_entered_
。当可以获取wlatch或rlatch时,用条件变量 writer_
或reader_
唤醒其他读写线程。需要注意的是析构函数在析构 mutex_
变量的时候,先使用了 guard
进行保护;其次是成员变量的声明顺序。
using mutex_t = std::mutex;
using cond_t = std::condition_variable;
static const uint32_t MAX_READERS = UINT_MAX;
public:
ReaderWriterLatch() = default;
~ReaderWriterLatch() {
std::lock_guard<mutex_t> guard(mutex_); }
private:
mutex_t mutex_;
cond_t writer_;
cond_t reader_;
uint32_t reader_count_{
0};
bool writer_entered_{
false};
获取读锁和释放读锁
获取读锁的时候,在第 6
行代码可以看到,当有写者进入或读者数量达到最大值时,新进入的读者会睡眠在条件变量 reader_
上;释放读锁的时候在 18~21
行首先检查是否有写者进入,如果释放后当前读者数量为0,则通知其中一个写线程,然后在 22~25
行检查当前读者数量,很明显当有读者持有该读锁时,其他读者也可以轻易获取该读锁,当释放该读锁后当前读者数量等于 MAX_READERS - 1
时,才可能会有其他读线程等待,此时需要唤醒它们。
01 /**
02 * Acquire a read latch.
03 */
04 void RLock() {
05 std::unique_lock<mutex_t> latch(mutex_);
06 while (writer_entered_ || reader_count_ == MAX_READERS) {
07 reader_.wait(latch);
08 }
09 reader_count_++;
10 }
11
12 /**
13 * Release a read latch.
14 */
15 void RUnlock() {
16 std::lock_guard<mutex_t> guard(mutex_);
17 reader_count_--;
18 if (writer_entered_) {
19 if (reader_count_ == 0) {
20 writer_.notify_one();
21 }
22 } else {
23 if (reader_count_ == MAX_READERS - 1) {
24 reader_.notify_one()