为啥这个名字看起来就像是ThreadLocal和Singleton的结合体,哈哈。
template<typename T>
class ThreadLocalSingleton : noncopyable
{
public:
ThreadLocalSingleton() = delete;
~ThreadLocalSingleton() = delete;
static T& instance()
{
if (!t_value_)
{
t_value_ = new T();
deleter_.set(t_value_);
}
return *t_value_;
}
static T* pointer()
{
return t_value_;
}
private:
static void destructor(void* obj)
{
assert(obj == t_value_);
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
T_must_be_complete_type dummy; (void) dummy;
delete t_value_;
t_value_ = 0;
}
class Deleter
{
public:
Deleter()
{
pthread_key_create(&pkey_, &ThreadLocalSingleton::destructor);
}
~Deleter()
{
pthread_key_delete(pkey_);
}
void set(T* newObj)
{
assert(pthread_getspecific(pkey_) == NULL);
pthread_setspecific(pkey_, newObj);
}
pthread_key_t pkey_;
};
static __thread T* t_value_;
static Deleter deleter_;
};
template<typename T>
__thread T* ThreadLocalSingleton<T>::t_value_ = 0;
template<typename T>
typename ThreadLocalSingleton<T>::Deleter ThreadLocalSingleton<T>::deleter_;
ThreadLocalSingleton和Singleton一样都是无法生成对象的工具类。因为构造函数和析构函数都被delete了。
ThreadLocalSingleton是用来辅助其他类生成线程单例的,也就是每个线程只有一个对象实例。这样可以将一些任务分配到各个线程,使用多线程提高执行任务的并发量。
该类的核心是使用线程局部存储,使用__thread创建线程局部变量。每个线程第一次调用instance时,因为t_value_==0,所以动态生成一个新对象,该对象只在本线程有效。不过这里使用了一个Deleter类,使用该类的set接口,将新创建的对象设置为线程特有数据。这里使用线程特有数据的原因好像只是为了利用创建线程特有数据键时注册的解构函数,在线程退出时删除动态创建的对象。
但是我觉得这种方式太麻烦了,即使用线程局部变量,又使用线程特有数据,感觉只使用线程特有数据会不会好一点,因为使用线程局部变量快吗??
我自己只使用线程特有数据模仿该类实现了一个类似的,代码如下:
#include <pthread.h>
template<typename T>
class ThreadLocalSingleton
{
public:
ThreadLocalSingleton() = delete;
~ThreadLocalSingleton() = delete;
static T& instance()
{
pthread_once(&ponce_, &ThreadLocalSingleton::init);
T* perThreadValue = static_cast<T*>(pthread_getspecific(pkey_));
if (!perThreadValue)
{
T* newObj = new T();
pthread_setspecific(pkey_, newObj);
perThreadValue = newObj;
}
return *perThreadValue;
}
private:
static void init()
{
pthread_key_create(&pkey_, &ThreadLocalSingleton::destructor);
}
static void destructor(void* obj)
{
T* obj2 = static_cast<T*>(obj);
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
T_must_be_complete_type dummy; (void) dummy;
delete obj2;
}
static pthread_key_t pkey_;
static pthread_once_t ponce_;
};
template<typename T>
pthread_once_t ThreadLocalSingleton<T>::ponce_ = PTHREAD_ONCE_INIT;
template<typename T>
pthread_key_t ThreadLocalSingleton<T>::pkey_;
然后又模仿ThreadLocalSingleton_test.cc写了一个测试程序:
#include "pthread_specific.h"
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <string>
int tid()
{
return static_cast<int>(::syscall(SYS_gettid));
}
class Test
{
public:
Test()
{
printf("tid=%d, constructing %p\n", tid(), this);
}
~Test()
{
printf("tid=%d, destructing %p %s\n", tid(), this, name_.c_str());
}
const std::string& name() const { return name_; }
void setName(const std::string& n) { name_ = n; }
private:
std::string name_;
};
void *threadFunc(void* changeTo)
{
const char *change = (const char *)changeTo;
printf("tid=%d, %p name=%s\n",
tid(),
&ThreadLocalSingleton<Test>::instance(),
ThreadLocalSingleton<Test>::instance().name().c_str());
ThreadLocalSingleton<Test>::instance().setName(change);
printf("tid=%d, %p name=%s\n",
tid(),
&ThreadLocalSingleton<Test>::instance(),
ThreadLocalSingleton<Test>::instance().name().c_str());
return NULL;
}
int main()
{
ThreadLocalSingleton<Test>::instance().setName("main one");
pthread_t tid1;
pthread_create(&tid1, NULL, threadFunc, (void *)"thread1");
pthread_t tid2;
pthread_create(&tid2, NULL, threadFunc, (void *)"thread2");
pthread_join(tid1, NULL);
printf("tid=%d, %p name=%s\n",
tid(),
&ThreadLocalSingleton<Test>::instance(),
ThreadLocalSingleton<Test>::instance().name().c_str());
pthread_join(tid2, NULL);
pthread_exit(0);
}
哈哈,只是想表达一下自己的观点,如果代码存在一些问题,或者我的想法本身有什么问题,希望有人看到可以提醒我。