先直接贴出代码:
#ifndef __WRITE_FIRST_RW_LOCK_H
#define __WRITE_FIRST_RW_LOCK_H
#include <mutex>
#include <condition_variable>
class WfirstRWLock
{
public:
WfirstRWLock() = default;
~WfirstRWLock() = default;
public:
void lock_read()
{
std::unique_lock<std::mutex> ulk(counter_mutex);
cond_r.wait(ulk, [=]()->bool {return write_cnt == 0; });
++read_cnt;
}
void lock_write()
{
std::unique_lock<std::mutex> ulk(counter_mutex);
++write_cnt;
cond_w.wait(ulk, [=]()->bool {return read_cnt == 0 && !inwriteflag; });
inwriteflag = true;
}
void release_read()
{
std::unique_lock<std::mutex> ulk(counter_mutex);
if (--read_cnt == 0 && write_cnt > 0)
{
cond_w.notify_one();
}
}
void release_write()
{
std::unique_lock<std::mutex> ulk(counter_mutex);
if (--write_cnt == 0)
{
cond_r.notify_all();
}
else
{
cond_w.notify_one();
}
inwriteflag = false;
}
private:
volatile size_t read_cnt{ 0 };
volatile size_t write_cnt{ 0 };
volatile bool inwriteflag{ false };
std::mutex counter_mutex;
std::condition_variable cond_w;
std::condition_variable cond_r;
};
template <typename _RWLockable>
class unique_writeguard
{
public:
explicit unique_writeguard(_RWLockable &rw_lockable)
: rw_lockable_(rw_lockable)
{
rw_lockable_.lock_write();
}
~unique_writeguard()
{
rw_lockable_.release_write();
}
private:
unique_writeguard() = delete;
unique_writeguard(const unique_writeguard&) = delete;
unique_writeguard& operator=(const unique_writeguard&) = delete;
private:
_RWLockable &rw_lockable_;
};
template <typename _RWLockable>
class unique_readguard
{
public:
explicit unique_readguard(_RWLockable &rw_lockable)
: rw_lockable_(rw_lockable)
{
rw_lockable_.lock_read();
}
~unique_readguard()
{
rw_lockable_.release_read();
}
private:
unique_readguard() = delete;
unique_readguard(const unique_readguard&) = delete;
unique_readguard& operator=(const unique_readguard&) = delete;
private:
_RWLockable &rw_lockable_;
};
#endif
可以看出用c++11实现读写锁变得非常简洁,在读取量非常大且写入频率很低的时候,通过一个简单的写入线程计数可以避免大量的 cond_w.notify_one();减少读取线程因此发生的切换。 这里需要注意的是对写操作加锁时是先增加写线程计数再判断条件并等待,释放写锁时减少写线程计数并判断是否还有写操作等待,如果有只能唤醒一个写等待。另外,直接使用lock、unlock在c++中是不推荐的,推荐采用RAII方法,即类unique_writeguard,unique_readguard。
此外,C++ 17 中提供了std::shared_mutex,可以实现了读写锁的功能。
libgo功能co_rwmutext.h中也实现了读写锁,性能比上述的还要好。libgo源码
#pragma once
#include "../common/config.h"
#include "../scheduler/processer.h"
#include <queue>
#include "co_condition_variable.h"
namespace co
{
/// 读写锁
class CoRWMutex
{
LFLock lock_;
long lockState_; // 0:无锁, >=1:读锁, -1:写锁
// 兼容原生线程
ConditionVariableAny rCv_;
ConditionVariableAny wCv_;
// 是否写优先
bool writePriority_;
public:
explicit CoRWMutex(bool writePriority = true);
~CoRWMutex();
void RLock();
bool RTryLock();
void RUnlock();
void WLock();
bool WTryLock();
void WUnlock();
bool IsLock();
private:
void TryWakeUp();
public:
class ReadView
{
friend class CoRWMutex;
CoRWMutex * self_;
public:
void lock();
bool try_lock();
bool is_lock();
void unlock();
ReadView() = default;
ReadView(ReadView const&) = delete;
ReadView& operator=(ReadView const&) = delete;
};
class WriteView
{
friend class CoRWMutex;
CoRWMutex * self_;
public:
void lock();
bool try_lock();
bool is_lock();
void unlock();
WriteView() = default;
WriteView(WriteView const&) = delete;
WriteView& operator=(WriteView const&) = delete;
};
ReadView& Reader();
WriteView& Writer();
// 兼容旧版接口
ReadView& reader();
WriteView& writer();
private:
ReadView readView_;
WriteView writeView_;
};
typedef CoRWMutex co_rwmutex;
typedef CoRWMutex::ReadView co_rmutex;
typedef CoRWMutex::WriteView co_wmutex;
} //namespace co
#include "co_rwmutex.h"
#include "../scheduler/scheduler.h"
namespace co
{
CoRWMutex::CoRWMutex(bool writePriority)
{
lockState_ = 0;
writePriority_ = writePriority;
readView_.self_ = writeView_.self_ = this;
}
CoRWMutex::~CoRWMutex()
{
assert(lock_.try_lock());
assert(lockState_ == 0);
readView_.self_ = writeView_.self_ = nullptr;
}
void CoRWMutex::RLock()
{
std::unique_lock<LFLock> lock(lock_);
retry:
if (writePriority_) {
// 写优先
if (lockState_ >= 0 && wCv_.empty()) {
++lockState_;
return ;
}
} else {
// 读优先
if (lockState_ >= 0) {
++lockState_;
return ;
}
}
rCv_.wait(lock);
goto retry;
}
bool CoRWMutex::RTryLock()
{
std::unique_lock<LFLock> lock(lock_);
if (lockState_ >= 0) {
++lockState_;
return true;
}
return false;
}
void CoRWMutex::RUnlock()
{
std::unique_lock<LFLock> lock(lock_);
assert(lockState_ > 0);
if (--lockState_ > 0)
return ;
TryWakeUp();
}
void CoRWMutex::WLock()
{
std::unique_lock<LFLock> lock(lock_);
retry:
if (lockState_ == 0) {
lockState_ = -1;
return ;
}
wCv_.wait(lock);
goto retry;
}
bool CoRWMutex::WTryLock()
{
std::unique_lock<LFLock> lock(lock_);
if (lockState_ == 0) {
lockState_ = -1;
return true;
}
return false;
}
void CoRWMutex::WUnlock()
{
std::unique_lock<LFLock> lock(lock_);
assert(lockState_ == -1);
lockState_ = 0;
TryWakeUp();
}
void CoRWMutex::TryWakeUp()
{
// 优先唤醒写等待
if (wCv_.notify_one())
return ;
// 唤醒读等待
rCv_.notify_all();
}
bool CoRWMutex::IsLock()
{
std::unique_lock<LFLock> lock(lock_);
return lockState_ == -1;
}
CoRWMutex::ReadView & CoRWMutex::Reader()
{
return readView_;
}
CoRWMutex::WriteView & CoRWMutex::Writer()
{
return writeView_;
}
CoRWMutex::ReadView & CoRWMutex::reader()
{
return readView_;
}
CoRWMutex::WriteView & CoRWMutex::writer()
{
return writeView_;
}
void CoRWMutex::ReadView::lock()
{
self_->RLock();
}
bool CoRWMutex::ReadView::try_lock()
{
return self_->RTryLock();
}
bool CoRWMutex::ReadView::is_lock()
{
return self_->IsLock();
}
void CoRWMutex::ReadView::unlock()
{
return self_->RUnlock();
}
void CoRWMutex::WriteView::lock()
{
self_->WLock();
}
bool CoRWMutex::WriteView::try_lock()
{
return self_->WTryLock();
}
bool CoRWMutex::WriteView::is_lock()
{
return self_->IsLock();
}
void CoRWMutex::WriteView::unlock()
{
self_->WUnlock();
}
} //namespace co