webrtc的锁,便于后记

本文介绍了WebRTC中互斥锁的实现,包括跨平台的CriticalSection类,资源管理类CritScope确保锁的自动释放,防止异常情况下资源泄露。同时提到了禁用拷贝和赋值的宏,以及AtomicOps类在原子操作中的应用。

今天想抓点什么优秀的代码,但是文件之间依赖性太大了,很难抽,看来看去,找到了一个地方。

锁的使用:

1,多线程访问同一个变量,需要加锁。

2,锁是一种资源,需要做好加锁和解锁相对应,创建和释放相对应。


下面是锁的代码:



#ifndef TALK_BASE_CRITICALSECTION_H__
#define TALK_BASE_CRITICALSECTION_H__


#include "talk/base/constructormagic.h"


#ifdef WIN32
//#include "talk/base/win32.h"
#include <Windows.h>
#endif


#ifdef POSIX
#include <pthread.h>
#endif


#ifdef _DEBUG
#define CS_TRACK_OWNER 1
#endif  // _DEBUG


#if CS_TRACK_OWNER
#define TRACK_OWNER(x) x
#else  // !CS_TRACK_OWNER
#define TRACK_OWNER(x)
#endif  // !CS_TRACK_OWNER


namespace talk_base {


#ifdef WIN32
class CriticalSection {
 public:
  CriticalSection() {
    InitializeCriticalSection(&crit_);
    // Windows docs say 0 is not a valid thread id
    TRACK_OWNER(thread_ = 0);
  }
  ~CriticalSection() {
    DeleteCriticalSection(&crit_);
  }
  void Enter() {
    EnterCriticalSection(&crit_);
    TRACK_OWNER(thread_ = GetCurrentThreadId());
  }
  bool TryEnter() {
    if (TryEnterCriticalSection(&crit_) != FALSE) {
      TRACK_OWNER(thread_ = GetCurrentThreadId());
      return true;
    }
    return false;
  }
  void Leave() {
    TRACK_OWNER(thread_ = 0);
    LeaveCriticalSection(&crit_);
  }


#if CS_TRACK_OWNER
  bool CurrentThreadIsOwner() const { return thread_ == GetCurrentThreadId(); }
#endif  // CS_TRACK_OWNER


 private:
  CRITICAL_SECTION crit_;
  TRACK_OWNER(DWORD thread_);  // The section's owning thread id
};
#endif // WIN32


#ifdef POSIX
class CriticalSection {
 public:
  CriticalSection() {
    pthread_mutexattr_t mutex_attribute;
    pthread_mutexattr_init(&mutex_attribute);
    pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&mutex_, &mutex_attribute);
    pthread_mutexattr_destroy(&mutex_attribute);
    TRACK_OWNER(thread_ = 0);
  }
  ~CriticalSection() {
    pthread_mutex_destroy(&mutex_);
  }
  void Enter() {
    pthread_mutex_lock(&mutex_);
    TRACK_OWNER(thread_ = pthread_self());
  }
  bool TryEnter() {
    if (pthread_mutex_trylock(&mutex_) == 0) {
      TRACK_OWNER(thread_ = pthread_self());
      return true;
    }
    return false;
  }
  void Leave() {
    TRACK_OWNER(thread_ = 0);
    pthread_mutex_unlock(&mutex_);
  }


#if CS_TRACK_OWNER
  bool CurrentThreadIsOwner() const { return pthread_equal(thread_, pthread_self()); }
#endif  // CS_TRACK_OWNER


 private:
  pthread_mutex_t mutex_;
  TRACK_OWNER(pthread_t thread_);
};
#endif // POSIX


// CritScope, for serializing execution through a scope.
class CritScope {
 public:
  explicit CritScope(CriticalSection *pcrit) {
    pcrit_ = pcrit;
    pcrit_->Enter();
  }
  ~CritScope() {
    pcrit_->Leave();
  }
 private:
  CriticalSection *pcrit_;
  DISALLOW_COPY_AND_ASSIGN(CritScope);
};


// Tries to lock a critical section on construction via
// CriticalSection::TryEnter, and unlocks on destruction if the
// lock was taken. Never blocks.
//
// IMPORTANT: Unlike CritScope, the lock may not be owned by this thread in
// subsequent code. Users *must* check locked() to determine if the
// lock was taken. If you're not calling locked(), you're doing it wrong!
class TryCritScope {
 public:
  explicit TryCritScope(CriticalSection *pcrit) {
    pcrit_ = pcrit;
    locked_ = pcrit_->TryEnter();
  }
  ~TryCritScope() {
    if (locked_) {
      pcrit_->Leave();
    }
  }
  bool locked() const {
    return locked_;
  }
 private:
  CriticalSection *pcrit_;
  bool locked_;
  DISALLOW_COPY_AND_ASSIGN(TryCritScope);
};


// TODO: Replace with platform-specific "atomic" ops.
// Something like: google3/base/atomicops.h TODO: And, move
// it to atomicops.h, which can't be done easily because of complex
// compile rules.
class AtomicOps {
 public:
#ifdef WIN32
  // Assumes sizeof(int) == sizeof(LONG), which it is on Win32 and Win64.
  static int Increment(int* i) {
    return ::InterlockedIncrement(reinterpret_cast<LONG*>(i));
  }
  static int Decrement(int* i) {
    return ::InterlockedDecrement(reinterpret_cast<LONG*>(i));
  }
#else
  static int Increment(int* i) {
    // Could be faster, and less readable:
    // static CriticalSection* crit = StaticCrit();
    // CritScope scope(crit);
    CritScope scope(StaticCrit());
    return ++(*i);
  }


  static int Decrement(int* i) {
    // Could be faster, and less readable:
    // static CriticalSection* crit = StaticCrit();
    // CritScope scope(crit);
    CritScope scope(StaticCrit());
    return --(*i);
  }


 private:
  static CriticalSection* StaticCrit() {
    static CriticalSection* crit = new CriticalSection();
    return crit;
  }
#endif
};


} // namespace talk_base


#endif // TALK_BASE_CRITICALSECTION_H__

主要有几个点

1,互斥锁类,跨平台

2,互斥锁资源管理类CritScope,等会介绍

3,抑制copy和赋值的宏

4,原子增减类


1,互斥锁类,没有什么可说的,主要有一个跟踪锁的线程所有者,还有一个跨平台的思想。相当不错。

2,互斥锁资源管理类CritScope,重点说,以局部变量管理资源的思想

上面的几点基本在effective c++中都有体现

使用CritScope对象来管理资源,它的源代码如下:
class CritScope {
 public:
  explicit CritScope(CriticalSection *pcrit) {//构造函数加锁
    pcrit_ = pcrit;
    pcrit_->Enter();
  }
  ~CritScope() {//析构函数解锁
    pcrit_->Leave();
  }
 private:
  CriticalSection *pcrit_;// CriticalSection类似于windows下的关键区和linux下的pthread_mutex_t类型的锁。
  DISALLOW_COPY_AND_ASSIGN(CritScope);
};


当需要向一个消息队列(消息队列中有一个CriticalSection类型的锁)添加消息的时候,我们的通常做法如下:
Void MessageQueue::Add(Message* msg)
{
m_pCrit->Lock();//加锁
message_queues_.push_back(msg);
m_pCrit->UnLock();//解锁
}
这样的代码就很容易出现当push_back出现异常的时候,m_pCrit没有被解锁,导致资源有问题。
这仅仅是一个例子,push_back一般不会出现异常,但是不能保证我们中间可能写了很多处理代码,难免不会有异常发生。
一旦发生,函数如果跳出,锁资源就不会被释放了。


Webrtc采用的方案:
Void MessageQueue::Add(Message* msg)
{
CritScope cs(&m_pCrit);//定义一个临时变量cs,它的构造函数中负责加锁,析构函数负责解锁。
message_queues_.push_back(msg);
//函数退出的时候,cs被释放,自动调用析构函数解锁
}
采用这种方式,可以保证资源一定会被正常的释放,即使中间代码出现异常,cs变量的析构函数还是会被正常调用的。
这种可能会对我们经常资源管理出现问题有些帮助。


3,抑制copy和赋值的宏

#ifndef LP_STUDY_CONSTRUCT_MAGIC_H
#define LP_STUDY_CONSTRUCT_MAGIC_H
#define DISABLE_ASSIGN(TypeName) \
void operator=(const TypeName&)
#define DISABLE_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
DISABLE_ASSIGN(TypeName)
#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
  DISALLOW_COPY_AND_ASSIGN(TypeName)
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
  TypeName();                                    \
  DISALLOW_EVIL_CONSTRUCTORS(TypeName) 
#endif//LP_STUDY_CONSTRUCT_MAGIC_H

4,原子增减类

这个不知道要用在什么地方,可以实现变量的原子增减,实际上就是加了一个锁而已。

曾经见过可以用它来实现信号量的pv操作的,现在还没有深入到内部。


总结:

感觉他们写代码特别注重代码的规范性和安全性,像资源管理和禁止copy和赋值,我平常都没有怎么用到过,虽然我对它的思想知道一些,但是编程的时候就是想不到去那样做,应该还是写的少的原因。这一次研究了一下,一方面为了复用,一方面可向此看齐。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值