功能与实现原理
允许多个线程同时加读锁,但只允许一个线程加写锁,可以写嵌套,即一个线程里面加写锁了,可以继续在这个线程加写锁,不会阻塞。
当有线程在读取时,写入线程阻塞,当写入线程执行时,所有的读取线程都被阻塞。
允许嵌套加锁
- 当前线程写入加锁时可以嵌套加读锁或写锁
- 当前线程读取加锁时可以嵌套加读锁,但不能嵌套加写锁,否则当前线程会陷入死锁,原理为:已经作为只读区了,就不应该存在写了
加读锁的时候:
- 判断当前线程是不是使用写锁的线程,如果是的话,可以加读锁。
- 如果当前线程不是使用写锁的线程,需要等待写锁线程使用完、以及正在等待写的先使用完写锁,因为写的优先级高。
加写锁的时候:
- 判断当前线程是不是使用写锁的线程,如果是的话,可以加写锁,因为允许嵌套写。
- 如果当前线程不是使用写锁的线程,需要等待读锁线程以及写锁线程都使用完,但可以不管正在等待读写的线程,因为自己先从条件变量抢到了锁。
释放读锁的时候:
释放写锁的时候:
- 注意嵌套写锁全都释放了以后,才是真正的释放。
- 释放后,如果有写锁正在等待,先通知写锁,因为写锁的优先级高。
- 如果有读锁正在等待,再通知读锁。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| void RWLock::ReadLock() { std::unique_lock<std::mutex> lck(m_mtx); if (std::this_thread::get_id() != this->m_writethreadid) { if (m_nwrite || m_nwriteWaiters) { ++m_nreadWaiters; while (m_nwrite || m_nwriteWaiters) m_rcond.wait(lck); --m_nreadWaiters; } } ++m_nread; }
void RWLock::ReadUnlock() { std::unique_lock<std::mutex> lck(m_mtx); --m_nread; if (m_nwriteWaiters) m_wcond.notify_one(); }
void RWLock::WriteLock() { std::unique_lock<std::mutex> lck(m_mtx); if (std::this_thread::get_id() != this->m_writethreadid) { if (m_nread || m_nwrite) { ++m_nwriteWaiters; while (m_nread || m_nwrite) m_wcond.wait(lck); --m_nwriteWaiters; } m_writethreadid = std::this_thread::get_id(); } ++m_nwrite; }
void RWLock::WriteUnlock() { std::unique_lock<std::mutex> lck(m_mtx); if (std::this_thread::get_id() != this->m_writethreadid) throw std::runtime_error("WriteLock/WriteUnlock mismatch");
--m_nwrite; if (0 == m_nwrite) { m_writethreadid = NULL_THEAD; if (m_nwriteWaiters) m_wcond.notify_one(); else if (m_nreadWaiters) m_rcond.notify_all(); } }
|