今天想抓点什么优秀的代码,但是文件之间依赖性太大了,很难抽,看来看去,找到了一个地方。
锁的使用:
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和赋值,我平常都没有怎么用到过,虽然我对它的思想知道一些,但是编程的时候就是想不到去那样做,应该还是写的少的原因。这一次研究了一下,一方面为了复用,一方面可向此看齐。
本文介绍了WebRTC中互斥锁的实现,包括跨平台的CriticalSection类,资源管理类CritScope确保锁的自动释放,防止异常情况下资源泄露。同时提到了禁用拷贝和赋值的宏,以及AtomicOps类在原子操作中的应用。
667

被折叠的 条评论
为什么被折叠?



