读写锁RWLock简单实现研究
个人随笔 (Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
引言
读写锁在服务端程序开发时,用的还是比较广泛的一种锁,比较适合用于多读少写的场景。多读少写场景,可以减少加锁时间;因为能够对大多数的读场景减少加锁时间,也一定程度减少多线程程序的编写难度。
分析读写锁的实现
读锁的约束项:
- 读锁时,需要能够计数,用于知道当前有多少个读锁在执行。
- 读锁加锁时,计数增加;
- 读锁解锁时,计数减少;
- 有写锁加锁时和加锁后,读锁不能加锁;
写锁的约束项:
- 写锁时,不需要计数
- 写锁之间互斥,一次只有一个写锁加锁成功;
- 写锁加锁和加锁后,之后的读锁加锁都等待;
- 写锁要加锁成功,之前的读锁都需要释放完毕,读锁计数为0;
基于上面的简单实现
满足上面提到的要求,可以通过一个atomic做读锁计数,一个mutex做读写锁互斥量 进行简单的实现。
简单实现如下:
读锁加锁时,快速加解锁:加锁,++计数,解锁
读锁解锁时,不加锁:计数–
写锁加锁时:加锁,等待计数为0
写锁解锁时:解锁
#include <stdio.h>
#include <mutex>
#include <atomic>
#include <thread>
#include <unistd.h>
class rwlock
{
public:
rwlock(){
_atomic.store(0);
}
void lockread(){
_mutex.lock();
_atomic++;
_mutex.unlock();
}
void unlockread(){
_atomic--;
}
void lockwrite(){
_mutex.lock();
while(_atomic.load() != 0){
continue;
}
}
void unlockwrite(){
_mutex.unlock();
}
private:
std::mutex _mutex;
std::atomic<int> _atomic;
};
进一步完善
进一步完善,主要集中在几个点上
- 写锁等待读锁完成时,使用信号量代替一直循环判断,可以在读锁unlockread时,发送信号量;或者读锁unlock发现计数为0时,发送信号量;写锁收到信号量后再判断;
- atomic的load, store,可以使用std::memory_order_relaxed,提高一点读写速度;
- 从测试效果看,mutex基本保证了申请锁次序与申请到锁次序一致性;否则的话,也要把这个次序考虑起来,这样就比较完善了;不会出现写锁与读锁前后申请,结果读锁先申请到了。
附加rwlock的测试程序与结果
测试程序:
rwlock lock;
void call1(int id){
printf("enter call rlock %d\n", id);
lock.lockread();
printf("lockread %d\n", id);
sleep(3);
lock.unlockread();
printf("unlockread %d\n", id);
}
void call2(int id){
printf("enter call wlock %d\n", id);
lock.lockwrite();
printf("lockwrite %d\n", id);
sleep(3);
lock.unlockwrite();
printf("unlockwrite %d\n", id);
}
int main(int argc, char** argv)
{
std::thread t1(call1, 1);
std::thread t2(call1, 2);
std::thread t3(call2, 3);
std::thread t4(call2, 4);
std::thread t5(call1, 5);
t1.join(); t2.join(); t3.join(); t4.join(); t5.join();
return 0;
}
测试结果:
enter call rlock 1
lockread 1
enter call rlock 2
enter call wlock 4
enter call rlock 5
enter call wlock 3
lockread 2
unlockread 1
unlockread 2
lockwrite 4
unlockwrite 4
lockread 5
unlockread 5
lockwrite 3
unlockwrite 3
个人随笔 (Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)