您的位置:首页 > 其它

Muduo之封装ThreadLocal、ThreadLocalSingleton

2018-02-08 15:22 162 查看
基础知识:

Linux线程基础知识

1、ThreadLocal

ThreadLocal可以叫做Thread的局部变量,通过ThreadLocal创建的对象每个线程都拥有其副本被各自线程独有,操作其对象不会造成线程不安全。这是通过封装Linux操作系统提供的线程特定数据实现,封装也比较简单,不涉及技巧。

template<typename T>
class ThreadLocal : boost::noncopyable//禁止拷贝和赋值
{
public:
ThreadLocal()
{
MCHECK(pthread_key_create(&pkey_, &ThreadLocal::destructor));
/*
创建线程键,此键由当前进程全部线程共有。
可以通过此键索引线程特定数据。
*/
}

~ThreadLocal()
{
MCHECK(pthread_key_delete(pkey_));//取消键与线程特定数据的关联
}

T& value()//APUE中常用做法
{
T* perThreadValue = static_cast<T*>(pthread_getspecific(pkey_));//获取特定数据区域
if (!perThreadValue)//如果没有分配对象空间,那么分配。
{
T* newObj = new T();//分配空间,并对应构造函数初始化
MCHECK(pthread_setspecific(pkey_, newObj));//将分配区域和键连接起来
perThreadValue = newObj;
}
return *perThreadValue;//返回对象的引用。
}

private:

static void destructor(void *x)//析构函数,x必须强制转换,告诉编译器内存组织关系
{
T* obj = static_cast<T*>(x);
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];//判断传入对象是否有定义
T_must_be_complete_type dummy;
(void) dummy;
delete obj;//对应new操作
}
private:
pthread_key_t pkey_;//线程键
};


2、ThreadLocalSingleton

__thread关键字

__thread
是GCC内置的线程局部存储设施,存取效率可以和全局变量相比。
__thread
变量每一个线程有一份独立实体,各个线程的值互不干扰。可以用来修饰那些“带有全局性且值可能变,但是又不值得用全局锁保护”的变量。

__thread
使用规则:只能修饰POD类型(类似整型指针的标量,不带自定义的构造、拷贝、赋值、析构的类型,二进制内容可以任意复制memset,memcpy,且内容可以复原),不能修饰class类型,因为无法自动调用构造函数和析构函数,可以用于修饰全局变量,函数内的静态变量,不能修饰函数的局部变量或者class的普通成员变量,且
__thread
变量值只能初始化为编译器常量。

__thread string t_obj1("Chen Shuo");//错误,不能调用对象的构造函数
__thread string* t_obj2 = new string;//错误,初始化必须用编译期常量
__thread string* t_obj3 = NULL;//正确,但是需要手工初始化并销毁对象
__thread int i = 5;//正确


#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
__thread int var = 1;//全局修饰,不必用锁保护,线程各自拥有副本
void* worker1(void* arg){
cout<< ++var <<endl;//输出2
}
void* worker2(void* arg){
cout<< ++var <<endl;//输出2
}
int main(){
pthread_t pid1,pid2;
static __thread  int temp=10;//修饰函数内的static变量
pthread_create(&pid1,NULL,worker1,NULL);
pthread_join(pid1,NULL);

pthread_create(&pid2,NULL,worker2,NULL);
pthread_join(pid2,NULL);
cout<< var <<endl;//输出1
return 0;
}


每一个线程都有各自的var,从输出可以看出。



线程本地单例类的实现,也比较简单,有了前面Singleton的实现,所以这里也很清楚。注意此实现用了
__thread
修饰指针已经确保的线程安全,所以没有用
pthread_once


template<typename T>
class ThreadLocalSingleton : boost::noncopyable//禁止拷贝,禁止赋值
{
public:

static T& instance()//返回单例对象,不需要按照线程安全方式实现,本身就是__thread类型线程安全
{
if (!t_value_)
{
t_value_ = new T();
deleter_.set(t_value_);
}
return *t_value_;
}

static T* pointer()//返回指针
{
return t_value_;
}

private:
ThreadLocalSingleton(); //禁止构造
~ThreadLocalSingleton();//禁止析构

static void destructor(void* obj)//通过RAII手法封装pthread_key_create和pthread_key_delete,供类调用
{
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_;//__thread关键字保证线程局部属性,只能修饰POD类型
static Deleter deleter_;//用来销毁T*指针所指对象
};

template<typename T>
__thread T* ThreadLocalSingleton<T>::t_value_ = 0;//初始化为空

template<typename T>
typename ThreadLocalSingleton<T>::Deleter ThreadLocalSingleton<T>::deleter_;//定义,然后默认调用Deleter构造函数,创建键。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: