linux 之 __thread & pthread_key_t
在说__thread之前,先来看看pthread_ket_t吧。
参考:http://blog.csdn.net/lmh12506/article/details/8452700
上面的博文说的比较通俗易懂。线程私有数据可以理解为线程内的全局变量。在线程内可以被所有函数访问,但是不能被其他线程的函数访问。
对于pthread_key_t的使用,最好使用RAII:
//博主参照muduo写的#include <pthread.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <boost/checked_delete.hpp>
template <typename T>
class ThreadLocal
{
public:
typedef ThreadLocal<T>* pThreadLocal; //指向模板的指针,应该用typedef定义一个别名来使用
private:
pthread_key_t pkey;
private:
static void destroy(void *x)
{
T *obj = static_cast <T*> (x);
boost::checked_delete(obj); //保证可以调用对象的析构函数
}
public:
ThreadLocal() //这些操作都放进构造和析构,自动执行
{
pthread_key_create(&pkey, destroy);
}
~ThreadLocal()
{
pthread_key_delete(pkey);
}
T& value() //始终保证槽内只有一个T类型对象
{
T* pThreadData = static_cast <T*> (pthread_getspecific(pkey));
if(pThreadData == NULL)
{
T* newData = new T();
pthread_setspecific(pkey, newData);
pThreadData = newData;
}
return *pThreadData;
}
};
class base
{
private:
int count = 4;
public:
void show()
{
count++;
std::cout << count << std::endl;
}
};
void* work(void* args)
{
ThreadLocal<base>::pThreadLocal p = static_cast<ThreadLocal<base>::pThreadLocal>(args); //p指向线程局部存储设施
base &pb = p->value(); //如果槽内没有对象,则构造一个对象。始终保证槽内存在一个对象。
pb.show(); //使用槽内的对象
}
int main()
{
std::vector<pthread_t> vec(2);
ThreadLocal<base>::pThreadLocal p = new ThreadLocal<base>; //先构造一个ThreadLocal对象,此对象封装了线程局部存储设施。
for(int i = 0; i < 2; ++i)
pthread_create(&vec[i], NULL, work, static_cast<void*>(p)); //拉两个线程,并把上述对象传递过去
for(int i = 0; i < 2; ++i)
pthread_join(vec[i], NULL);
delete p;
}
输出结果为 5和5 。可见两个线程未互相影响,base对象是存放在各自线程局部存储槽内的
__thread
陈硕在书中提了这个关键字,说是比pthread_key_t效率高
根据书中的说法:GCC内置的线程局部存储设施。只能用于修饰POD类型,不能修饰class类型(即不能修饰一个对象),因为无法自动调用构造函数和析构函数(有道理,毕竟是线程局部存储区域,c++的魔抓可能伸不到这里来)。__thread可以修饰全局变量,函数内的静态变量,但是不能用于修饰函数的局部变量(上面的pthread_key_t可以,用来存储一个栈上的流对象)或者class的普通成员变量。另外,__thread变量的初始化只能用编译期常量(new的话就别想了).
__thread string str; //error,不能调用对象的构造函数
__thread string *pStr = new string; //error,初始化必须用编译期常量
__thread变量是每个线程有一份独立的实体,各个线程的变量值互不干扰。还有个用途:用来修饰“值可能会变,带有全局性,但是又不值得用全局锁保护”的变量。如果一个值,想在线程内被所有函数访问,但又不想被其他线程影响,可以试试__thread.
总之,用法不难,最重要的是要知道在什么时候用。这些都得靠经验之积累。
POD类型:
POD 类型(纯旧数据):C++ 中的此类非正式数据类型类别是指作为标量(参见基础类型部分)的类型或 POD 类。 POD 类没有不是 POD 的静态数据成员,没有用户定义的构造函数、用户定义的析构函数或用户定义的赋值运算符。 此外,POD 类无虚函数、基类、私有的或受保护的非静态数据成员。 POD 类型通常用于外部数据交换,例如与用 C 语言编写的模块(仅具有 POD 类型)进行的数据交换。