Linux下读写锁的实现
读写锁顾名思义就是用于读/写操作竞争的锁
有这么一种情况 一路在写资源 多路在读资源
使用之前分析过的临界锁和事件实现
临界锁的实现 http://blog.csdn.net/qq_21358401/article/details/78638668
事件的实现 http://blog.csdn.net/qq_21358401/article/details/78623793
接口实现
实现文件: webRTC/base/sharedexclusivelock.cc
文件内实现了三个类 SharedExclusiveLock SharedScope ExclusiveScope
ShareScope 和 ExclusiveScope是对SharedExclusiveLock 的封装
从名字上也可以看出 SharedScope 共享区 是读文件/资源的接口
ExclusiveScope 排他区 是写文件/资源的接口
class LOCKABLE SharedExclusiveLock {
public:
SharedExclusiveLock();
// Locking/unlocking methods. It is encouraged to use SharedScope or
// ExclusiveScope for protection.
void LockExclusive() EXCLUSIVE_LOCK_FUNCTION();
void UnlockExclusive() UNLOCK_FUNCTION();
void LockShared();
void UnlockShared();
private:
rtc::CriticalSection cs_exclusive_; // 排他 临界锁
rtc::CriticalSection cs_shared_; // 共享 临界锁
rtc::Event shared_count_is_zero_; // 排他 事件
int shared_count_; // 共享锁 计数
RTC_DISALLOW_COPY_AND_ASSIGN(SharedExclusiveLock);
};
首先看读写锁的构造函数
SharedExclusiveLock::SharedExclusiveLock()
: shared_count_is_zero_(true, true), // 注意Event事件shared_cound_is_zero的构造参数
shared_count_(0) {
}
构造时指示了Event事件的构造参数时true 这也就意味着在事件没有reset之前 即使调用事件的Wait 也并不会阻塞等待
这个逻辑 正符合读写锁的逻辑
写文件/资源时 如果没有线程在读文件/资源 则直接写文件/资源
如果有线程在读文件/资源 则等待所有读动作退出后 才写资源(也就是读写锁中shared_count_对读文件/资源的共享区计数的意义所在)
再看排他区的操作
void SharedExclusiveLock::LockExclusive() {
cs_exclusive_.Enter(); // 进入临界区
shared_count_is_zero_.Wait(Event::kForever); // 等待读动作结束或直接继续写动作(没有读动作的情况下)
}
void SharedExclusiveLock::UnlockExclusive() {
cs_exclusive_.Leave(); //退出临界区
}
而共享区的操作
void SharedExclusiveLock::LockShared() {
CritScope exclusive_scope(&cs_exclusive_);
CritScope shared_scope(&cs_shared_); // 进入共享区(之前分析过 临界锁是可以重入的)
if (++shared_count_ == 1) { // 第一次进入 则reset事件
// reset后 事件wait会阻塞等待事件set
// 也就是有读动作则写动作阻塞等待
shared_count_is_zero_.Reset();
}
}
void SharedExclusiveLock::UnlockShared() {
CritScope shared_scope(&cs_shared_);
if (--shared_count_ == 0) { // 最后一个读动作也退出
// 事件set 使得写动作得以执行
shared_count_is_zero_.Set();
}
}
示例代码:
#include <iostream>
#include <memory>
#include <string>
#include <csignal>
#include "pthread.h"
#include "webrtc/base/sharedexclusivelock.h"
using namespace rtc;
using namespace std;
SharedExclusiveLock *lock;
static int val = 0;
void *thread_write(void *args) {
int cnt = 0;
for (; cnt < 5; cnt++) {
lock->LockExclusive();
val++;
lock->UnlockExclusive();
}
}
void *thread_read(void *args) {
int cnt = 0;
for (; cnt < 5; cnt++) {
lock->LockShared();
cout << "read val:" << val << endl;
lock->UnlockShared();
}
}
int main(int argc, char **argv) {
int ret = 0;
pthread_t t_write, t_read;
lock = new SharedExclusiveLock();
ret = pthread_create(&t_write, NULL, thread_write, NULL);
if (ret < 0) {
cout << "create thread write failed"<< endl;
return -1;
}
ret = pthread_create(&t_read, NULL, thread_read, NULL);
if (ret < 0) {
cout << "create thread read failed"<< endl;
return -1;
}
pthread_join(t_write, NULL);
pthread_join(t_read, NULL);
return 0;
}
执行结果一般是输出读到的都是资源数都是5或者都是0
这是因为读动作触发事件 使得写事件被阻塞 -> 读到的都是0
或者写动作快速完成 -> 读动作执行 -> 读到的都是5
git参考代码地址
https://github.com/sliver-chen/webRTCTutorial/blob/master/SharedScope/SharedScope.cpp