threading命名空间
- 位置:位于include/log4cpp/threading目录下
- 相应类都处于threading命名空间下。这个命名空间中包含了一些线程同步的类,以及获取当前线程的方法。还有一个线程作用域局部变量的实现
- 因为线程是一个系统内核对象,并且不同的编译器平台也对线程这个借口进行了不同的封装,所以他有很多种实现,也就是不同平台的具体实现类的名称可能不一样。这里屏蔽了不同提供了同一的名称:Mutex, ScopedLock, ThreadLocalDataHolder, getThreadID
win平台
- 位置:include/log4cpp/threading/MSThreads.xx
Mutex类
- 作用:这个类实现的是互斥量的功能,可以用来保护某些访问是线程安全的。下面看一下他在win32平台上代码的实现。
class MSMutex {
public:
//在构造器中创建临界区对象
MSMutex() {
InitializeCriticalSection(&_criticalSection);
}
// 在析构器中销毁临界区对象
~MSMutex() {
DeleteCriticalSection(&_criticalSection);
}
// 返回包装的临界区对象
inline LPCRITICAL_SECTION getCriticalSection() {
return &_criticalSection;
}
private:
//禁止拷贝,否则容易出现死锁(因为操作不一致)
MSMutex(const MSMutex&);
CRITICAL_SECTION _criticalSection; //临界区对象
};
typedef MSMutex Mutex; // 一个简单非可递归的锁
可以看到,Mutex就是一个MSMutex的别名,MSMutex的实现,内部其实就是包装了一个临界区对象,在对象被构造的时候创建该对象,在对象被析构时销毁该对象。并且提供了一个让用户访问的接口getCriticalSection方法,最后还定义了两个私有的方法来禁止该对象被拷贝
ScopedLock类
//一个作用域局部锁
class MSScopedLock {
public:
//在构造器中进入锁
MSScopedLock(MSMutex &mutex) {
_criticalSection = mutex.getCriticalSection();
EnterCriticalSection(_criticalSection);
}
// 在析构器中释放锁
~MSScopedLock() {
LeaveCriticalSection(_criticalSection);
}
private:
MSScopedLock(const MSScopedLock& other); // 进制对象被拷贝
LPCRITICAL_SECTION _criticalSection; // 内部保存的临界区对象指针
};
typedef MSScopedLock ScopedLock; // 这儿是定义的ScopedLock的统一名称类型
可以看到ScopedLock的真正类型是MSCopedLock,逻辑很简单,必须要使用Mutex对象进行构造然后进入该临界区对象,然后,再次ScopedLock对象性被析构时,离开临界区,这就能保证某个作用域内的方法是安全的。比如
class Category {
public:
void addAppender(Appender *appender) {
ScopedLock scopedLock(_mutex);
_appenders.insert(appender);
}
void removeAppender(Appender *appender) {
ScopedLock scopedLock(_mutex);
auto pos = _appenders.find(appender);
if (pos != _appenders.end()) {
delete appender;
_appenders.erase(pos);
}
}
private:
Mutex _mutex;
std::set<Appender*> _appenders;
};
这样实现可以保证对_appenders的访问是线程安全的
ThreadLocalDataHolder
这个类就是java中线程局部类的实现,它不同于java中的哪种实现方式,这个类完全就是包装系统的tls方面API函数,另外还提供了智能指针方面的接口
template <typename T>
class ThreadLocalDataHolder {
public:
//在构造的时候,调用库函数来分配一个线程局部变量的key
ThreadLocalDataHolder() {
_key = TlsAlloc();
}
// 在析构的时候,销毁线程局部key
~ThreadLocalDataHolder()
{
TlsFree(_key); //这儿有个疑问,难道不用回收对应key所对应的值吗?????
}
// 获取当前线程所拥有的T类型的值
//返回线程所私有的T的指针,或者如果线程没有设置线程局部的话,则返回false
inline T* get() const{
return (T*)TlsGetValue(_key);
}
// 获取当前线程的线程局部变量
//这样设计的好处是可以将ThreadLocalDataHolder看做是一个智能指针
inline T *operator->() const{
return get();
}
// 返回当前线程的线程局部变量,这儿返回的是引用
inline T &operator*() const{
return *get();
}
// 释放当前线程的线程局部变量
// 返回的是当前线程所有的局部变量,或者为nullptr(如果线程没有设置值的话)
inline T *release() {
T *result = TlsGetValue(_key);
TlsSetValue(_key, nullptr);
return result;
}
// 重新设置线程局部变量的值
//首先会先删除原来的值,然后再设置新的值
inline void reset(T *p = nullptr) {
T *thing = (T*)TlsGetValue(_key);
delete thing;
TlsSetValue(_key, p);
}
private:
DWORD _key;
};
getThreadID函数
std::string getThreadID()
{
char buf[16];
sprintf_s(buf, "%lu", ::GetCurrentThreadId());
return std::string(buf);
}
就是调用系统的api函数GetCurrentThreadId()来获取当前线程标识符,因为此标识符是DWORD类型,然后调用格式化函数将 这个DWORD类型转换为字符串,并将其返回。
linux平台
- 位置:include/log4cpp/threading/PThreads.xx
Mutex
class Mutex {
private:
pthread_mutex_t mutex;
public:
inline Mutex() {
::pthread_mutex_init(&mutex, NULL);
}
inline void lock() {
::pthread_mutex_lock(&mutex);
}
inline void unlock() {
::pthread_mutex_unlock(&mutex);
}
inline ~Mutex() {
::pthread_mutex_destroy(&mutex);
}
private:
Mutex(const Mutex& m);
Mutex& operator=(const Mutex &m);
};
ScopedLock
class ScopedLock {
private:
Mutex& _mutex;
public:
inline ScopedLock(Mutex& mutex) :
_mutex(mutex) {
_mutex.lock();
}
inline ~ScopedLock() {
_mutex.unlock();
}
};
ThreadLocalDataHolder
template<typename T>
class ThreadLocalDataHolder {
private:
pthread_key_t _key;
public:
typedef T data_type;
inline ThreadLocalDataHolder() {
::pthread_key_create(&_key, freeHolder);
}
inline static void freeHolder(void *p) {
assert(p != NULL);
delete reinterpret_cast<T *>(p);
}
inline ~ThreadLocalDataHolder() {
T *data = get();
if (data != NULL) {
delete data;
}
::pthread_key_delete(_key);
}
inline T* get() const {
return reinterpret_cast<T *>(::pthread_getspecific(_key));
}
inline T* operator->() const { return get(); }
inline T& operator*() const { return *get(); }
inline T* release() {
T* result = get();
::pthread_setspecific(_key, NULL);
return result;
}
inline void reset(T* p = NULL) {
T *data = get();
if (data != NULL) {
delete data;
}
::pthread_setspecific(_key, p);
}
};
总结
threading命名空间内的一些工具类和函数,包含:
- Mutex实现的是一个平台无关的互斥量,说它平台无关是因为每一个平台都会实现自己的Mutex
- ScopedLock实现指定作用域的自动加锁、自动解锁操作
- ThreadLocalDataHolder 一个模板类,实现了线程局部类,主要注意的是,当不在使用此对象之前,必须要释放掉,存进此对象的数据。
- getThreadID 获取的是线程标识符的字符串格式。