SharedExclusiveLock 共享互斥锁

共享互斥锁,有时也被称为单写多读锁。
简单点的应用场景就是:一个写多个读。

代码来源是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解决之后,就可以读读同时进行了。

好代码一定要细细品味 冷暖自知

你可能感兴趣的:(源码分析)