共享互斥锁,有时也被称为单写多读锁。
简单点的应用场景就是:一个写多个读。
代码来源是webrtc的SharedExclusiveLock类
// This class provides shared-exclusive lock. It can be used in cases like
// multiple-readers/single-writer model.
class SharedExclusiveLock {
public:
SharedExclusiveLock();
// Locking/unlocking methods. It is encouraged to use SharedScope or
// ExclusiveScope for protection.
void LockExclusive();
void UnlockExclusive();
void LockShared();
void UnlockShared();
private:
rtc::CriticalSection cs_exclusive_;
rtc::CriticalSection cs_shared_;
rtc::Event shared_count_is_zero_;
int shared_count_;
};
这个是头文件,下面是cpp:
SharedExclusiveLock::SharedExclusiveLock()
: shared_count_is_zero_(true, true),
shared_count_(0) {
}
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) {
shared_count_is_zero_.Reset();
}
}
void SharedExclusiveLock::UnlockShared() {
CritScope shared_scope(&cs_shared_);
if (--shared_count_ == 0) {
shared_count_is_zero_.Set();
}
}
至于rtc下的CriticalSection和Event,就是将windows下的互斥量和事件简单封装了一下,不贴代码也可以猜到调用了哪些方法。
下面是SharedExclusiveLock的用法:
class SharedScope {
public:
explicit SharedScope(SharedExclusiveLock* lock)
: lock_(lock) {
lock_->LockShared();
}
~SharedScope() { lock_->UnlockShared(); }
private:
SharedExclusiveLock* lock_;
};
class ExclusiveScope {
public:
explicit ExclusiveScope(SharedExclusiveLock* lock)
: lock_(lock) {
lock_->LockExclusive();
}
~ExclusiveScope() { lock_->UnlockExclusive(); }
private:
SharedExclusiveLock* lock_;
};
当时不是调试代码到这的, 而是画类图看到这了,盯了半小时,在记事本上画了一页之后才有个概念:
结合注释知道这是一个单写多读的锁,那么以下几点应该是可预见的:
读时不能写,写时不能读,读读是允许的
反向查了一下代码,发现在webrtc中比较常见的用法如下:
SharedExclusiveLock ss_lock_;
// 001
SharedScope ss(&ss_lock_);
if (ss_) {
ss_->SetMessageQueue(NULL);
// 002
SocketServer* MessageQueue::socketserver() {
SharedScope ss(&ss_lock_);
return ss_;
}
// 003
ExclusiveScope es(&ss_lock_);
ss_ = ss ? ss : own_ss_.get();
ss_->SetMessageQueue(this);
// 004
{
// Wait and multiplex in the meantime
SharedScope ss(&ss_lock_);
if (!ss_->Wait(static_cast<int>(cmsNext), process_io))
return false;
}
看了用法结合源码,分析如下:
SharedExclusiveLock有两互斥两和一个事件,外加一个共享计数
取下名字好接下来分析:互斥量A 互斥两B 事件E
互斥量A主要用于读写之间,互斥量B用于读读之间,且B这样设计是为了保证共享计数的同步
至于E,就是和A配合来保证读写同步
看下源码:
写:
LockExclusive
。。。
UnlockExclusive
过程是获取A,等待E有信号,写操作,释放A
读:
LockShared
。。。
UnlockShared
过程是:等待A,更新共享计数,释放A,读操作
作用域完了之后:更新共享计数
可以看出读和写之间的同步是通过一个互斥量A和一个事件E来保证的
当写时,读是被阻塞的,具体是因为A还被写操作持有,读会阻塞
当读时,写是被阻塞的,具体是因为事件被置为无信号,读会等待
当读时,遇到了另一个读操作,她们俩之间只会将因为共享技术有些竞争,用互斥量B解决之后,就可以读读同时进行了。
好代码一定要细细品味 冷暖自知