读写锁是一种特殊的锁,用于保护共享资源的读写操作。读写锁允许多个线程同时读取共享资源,但是只允许一个线程进行写操作,从而提高了并发性能。读写锁的实现原理如下:
-
读锁的获取:当一个线程请求读锁时,如果当前没有写锁或者写锁的持有者和请求读锁的线程是同一个线程,那么该线程可以获得读锁,否则该线程会被阻塞,直到写锁被释放。
-
写锁的获取:当一个线程请求写锁时,如果当前没有读锁或者写锁,那么该线程可以获得写锁,否则该线程会被阻塞,直到读锁和写锁都被释放。
-
读锁的释放:当一个线程释放读锁时,如果没有其他线程持有写锁,那么该线程释放读锁,否则该线程继续持有读锁。
-
写锁的释放:当一个线程释放写锁时,所有等待读锁和写锁的线程都可以尝试获取锁。
在这个实现中,读锁和写锁都使用了互斥锁和条件变量来实现。当一个线程请求读锁时,如果当前有写锁,该线程会被阻塞,直到写锁被释放;当一个线程请求写锁时,如果当前有读锁或者写锁,该线程会被阻塞,直到读锁和写锁都被释放。读锁和写锁的释放都会唤醒等待的线程。
#include <mutex>
#include <condition_variable>
#include "../base/debug_view.h"
class RWLock {
private:
std::mutex mtx;
std::condition_variable read_cv;
std::condition_variable write_cv;
int read_cnt;
int write_cnt;
public:
void read_lock() {
std::unique_lock<std::mutex> lock(mtx);
zz_log("有个读者,期望进行读操作");
if (write_cnt > 0) {
zz_log("当前存在写入操作,数量为: %d, 不可以读操作", write_cnt);
read_cv.wait(lock);
}
zz_log("当前没有写入操作,可读,读者数量: %d", read_cnt + 1);
++read_cnt;
}
void read_unlock() {
std::unique_lock<std::mutex> lock(mtx);
--read_cnt;
zz_log("有一个读者读完了,减少一个读者数量: %d", read_cnt);
if (read_cnt == 0) {
zz_log("不存在读者,立即通知写操作执行, 仅通知一个");
write_cv.notify_one();
}
if (read_cnt > 0) {
zz_log("仍存在读者,在读数据,不可以通知写操作执行");
}
}
void write_lock() {
std::unique_lock<std::mutex> lock(mtx);
++write_cnt;
zz_log("有个写者,期望进行写操作, 当前写者数量: %d", write_cnt);
if (read_cnt > 0) {
zz_log("当前存在读者,不可以写操作");
write_cv.wait(lock);
}
else if (write_cnt > 0) {
zz_log("当前存在其他写者,不可以写操作");
write_cv.wait(lock);
}
zz_log("可以进行写操作 --->");
}
void write_unlock() {
std::unique_lock<std::mutex> lock(mtx);
--write_cnt;
zz_log("有一个写者写完了,释放独占锁");
if (write_cnt == 0) {
zz_log("当前已经不存在等待的写者进行写操作了,可以通知等待的读者进行读操作了");
read_cv.notify_all();
}
if (write_cnt > 0) {
zz_log("当前还有写者在等待执行写操作,通知一个写者来执行!!!");
write_cv.notify_one();
}
}
public:
/* 1.构造函数 */
RWLock() : read_cnt(0), write_cnt(0)
{
zz_log("1 构造函数 addr: %p", this);
}
/* 7.析构函数 */
~RWLock()
{
zz_log("7 析构函数 addr: %p", this);
}
private:
};