关键字:__thread & pthread_key_t
在说__thread之前,先来看看pthread_ket_t吧。
参考:http://blog.csdn.net/lmh12506/article/details/8452700
上面的博文说的比较通俗易懂。线程私有数据可以理解为线程内的全局变量。在线程内可以被所有函数访问,但是不能被其他线程的函数访问。
这里博主直接去找pthread.h头文件中的API,发现写的还是很详细的。
/* Functions for handling thread-specific data. */ //用于处理线程特定数据的函数。 /* Create a key value identifying a location in the thread-specific //identifying 识别
data area. Each thread maintains a distinct thread-specific data //maintains 维护 distinct 不同的
area. DESTR_FUNCTION, if non-NULL, is called with the value
associated to that key when the key is destroyed. //associated to 关联 即__destr_function如果不是null,则被关联到key销毁的时刻
DESTR_FUNCTION is not called if the value associated is NULL when
the key is destroyed. */
extern int pthread_key_create (pthread_key_t *__key,
void (*__destr_function) (void *))
__THROW __nonnull (()); /* Destroy KEY. */
extern int pthread_key_delete (pthread_key_t __key) __THROW; /* Return current value of the thread-specific data slot identified by KEY. */ //slot 槽 这个理解好,把线程局部存储设施比喻为槽。
extern void *pthread_getspecific (pthread_key_t __key) __THROW; //注意这个返回值,使用时记得把void* 转换成指定的类型 /* Store POINTER in the thread-specific data slot identified by KEY. */ //往槽里存储数据
extern int pthread_setspecific (pthread_key_t __key,
const void *__pointer) __THROW ;
贴一段博主自己在电脑上的测试:
#include <pthread.h>
#include <iostream>
#include <vector>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
using namespace std; static pthread_key_t pkt;
//FILE* log_handle; void destroy(void *arg)
{
// sleep(10);
fclose((FILE*)arg); // <1>
} void write_log(const char* log)
{
FILE* log_handle = reinterpret_cast<FILE*> (pthread_getspecific(pkt)); //从槽中拿值
fprintf(log_handle, "%s\n", log);
} void* work(void *arg)
{
FILE *log_handle;
char file_name[128] = ""; sprintf(file_name, "/home/ttsj/log/Thread%d.log", static_cast<int>(pthread_self())); cout << file_name << endl;
log_handle = fopen(file_name, "w"); pthread_setspecific(pkt, reinterpret_cast<void*> (log_handle)); //向线程私有存储槽中存入值 write_log("Thread starting."); //work here...
} int main()
{
vector<pthread_t> savepid; pthread_key_create(&pkt, destroy); //<2> int i;
for(i = ; i < ; ++i)
{
savepid.push_back(i);
} for(i = ; i < ; ++i)
{
pthread_create(&savepid[i], NULL, work, NULL); //拉5个线程
} for(i = ; i < ; ++i)
{
cout << "join" << i << endl;
pthread_join(savepid[i], NULL);
} pthread_key_delete(pkt);
sleep(); return ;
}
其实使用没什么难的,但博主却折腾了两个小时,知道为什么吗?请仔细看看<1>和<2>.刚开始就是不知道如何给销毁函数destroy传递参数,而不传递参数,又如何关闭流呢?经过博主两个小时的验证,才搞明白了:看<1>处,得知这个参数根本不用传,系统自动给你传了,而传递的值是什么呢?哈哈,就是线程私有数据!看下面,在程序运行时,destroy成功通过参数关闭了文件流,说明系统确实把线程私有数据(打开的文件流)传递给了pthread_create的第二个参数,而这一切都不用我们自己动手了!

对于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 = ;
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();
ThreadLocal<base>::pThreadLocal p = new ThreadLocal<base>; //先构造一个ThreadLocal对象,此对象封装了线程局部存储设施。 for(int i = ; i < ; ++i)
pthread_create(&vec[i], NULL, work, static_cast<void*>(p)); //拉两个线程,并把上述对象传递过去
for(int i = ; i < ; ++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 类型)进行的数据交换。
pthread_join等待多个线程问题:
for(int i =; i < ; ++i) pthread_join(pid[i],NULL)
实际上主线程在pthread_join(1,NULL);这里就挂起了,在等待1号线程结束后再等待2号线程。在等待玩1后,2345线程可能已经结束了,但主线程依然可以回收。 checked_delete提升安全性
依然先贴代码:
//curr.h
class prev;
class curr
{
public:
void Delete(prev* p) {
delete p; //此处删除,无法调用prev的析构函数,即prev没有被真正销毁。因为curr不知道prev的详细定义。编译器会给出警告,但不会报错。造成内存泄露
}
};
// prev.h
class prev
{
public:
~prev() {
cout << "delete prev" << endl;
}; int main()
{
curr obj;
obj.Delete(new obj);
}
这里应该可以使用boost的checked_delete(T *p) 进行检查。对不完全类的删除,都应该用这个去删除。当然最好直接用智能指针。贴一下cheched_delete的boost源码:
template<class T> inline void checked_delete(T * x)
{
// intentionally complex - simplification causes regressions
typedef char type_must_be_complete[ sizeof(T)? : - ]; //如果是不完全类型,则[]内是-1,[-1]是不允许的
(void) sizeof(type_must_be_complete);
delete x;
} template<class T> inline void checked_array_delete(T * x)
{
typedef char type_must_be_complete[ sizeof(T)? : - ];
(void) sizeof(type_must_be_complete);
delete [] x;
} template<class T> struct checked_deleter
{
typedef void result_type;
typedef T * argument_type; void operator()(T * x) const
{
// boost:: disables ADL
boost::checked_delete(x);
}
}; template<class T> struct checked_array_deleter
{
typedef void result_type;
typedef T * argument_type; void operator()(T * x) const
{
boost::checked_array_delete(x);
}
};
用法很简单。当然,你得首先安装一下boost库:
#include <boost/checked_delete.hpp> void Delete(prev *p)
{
boost::checked_delete(p);
}
关键字:__thread & pthread_key_t的更多相关文章
- Mudo C++网络库第四章学习笔记
C++多线程系统编程精要 学习多线程编程面临的最大思维方式的转变有两点: 当前线程可能被切换出去, 或者说被抢占(preempt)了; 多线程程序中事件的发生顺序不再有全局统一的先后关系; 当线程被切 ...
- 线程局部存储tls的使用
线程局部存储(Thread Local Storage,TLS)主要用于在多线程中,存储和维护一些线程相关的数据,存储的数据会被关联到当前线程中去,并不需要锁来维护.. 因此也没有多线程间资源竞争问题 ...
- 作为一个新手的Oracle(DBA)学习笔记【转】
一.Oracle的使用 1).启动 *DQL:数据查询语言 *DML:数据操作语言 *DDL:数据定义语言 DCL:数据控制语言 TPL:事务处理语言 CCL:指针控制语言 1.登录 Win+R—cm ...
- gcc 中__thread 关键字的示例代码
__thread 关键字的解释: Thread Local Storage 线程局部存储(tls)是一种机制,通过这一机制分配的变量,每个当前线程有一个该变量的实例. gcc用于实现tls的运行 ...
- 线程局部存储空间 pthread_key_t、__thread 即 ThreadLocal
https://www.jianshu.com/p/495ea7ce649b?utm_source=oschina-app 该博客还未学习完 还有 pthread_key_t Thread ...
- Qt:用 __thread 关键字让每个线程有自己的全局变量
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/wsj18808050/article/d ...
- __thread关键字[转]
自 http://blog.csdn.net/liuxuejiang158blog/article/details/14100897 __thread是GCC内置的线程局部存储设施,存取效率可以和全局 ...
- [转] __thread关键字
http://blog.csdn.net/liuxuejiang158blog/article/details/14100897 __thread是GCC内置的线程局部存储设施,存取效率可以和全局变量 ...
- 多线程编程--- __thread关键字
__thread是GCC内置的线程局部存储设施,存取效率可以和全局变量相比.__thread变量每一个线程有一份独立实体,各个线程的值互不干扰.可以用来修饰那些带有全局性且值可能变,但是又不值得用全局 ...
随机推荐
- 小D课堂 - 零基础入门SpringBoot2.X到实战_第9节 SpringBoot2.x整合Redis实战_38、源码编译安装Redis4.x
笔记 2.源码编译安装Redis4.x 简介:使用源码安装Redis4.x和配置外网访问 1.快速安装 https://redis.io/download#installation ...
- Docs-.NET-C#-指南-语言参考-关键字-值类型:enum
ylbtech-Docs-.NET-C#-指南-语言参考-关键字-值类型:enum 1.返回顶部 1. enum(C# 参考) 2015/07/20 enum 关键字用于声明枚举,一种包含一组被称为枚 ...
- ISO/IEC 9899:2011 条款6.7.4——函数说明符
6.7.4 函数说明符 语法 1.function-specifier: inline _Noreturn 约束 2.函数说明符应该只能被用在对一个函数标识符的声明中. 3.对一个含有外部连接函数的内 ...
- Python3基础 函数 参数 多个参数都有缺省值,需要指定参数进行赋值
Python : 3.7.3 OS : Ubuntu 18.04.2 LTS IDE : pycharm-community-2019.1.3 ...
- Access 字段拼接(UPDATE 数据追加)
今天遇到一个需求,在Access数据库中,有个net_id 字段,它的值是由 “jjgrape” 这个字符串和 id 字段组成的,也就是说,要把 ‘jjgrape’ 和 id 字段拼接起来: 那怎么拼 ...
- 论H5嵌入APP的联合登录的解决方案
什么是联合登录 因为公司产品的发展,会与第三方的一些商户进行对接,商户APP提供入口,进入我们的H5页,从而提供服务. 而商户希望用户在其APP进行账户登录后,进入H5页不再进行登录,所以我们的H5需 ...
- Visual Studio Code + Docker
前言 VS Code是一个年轻的编辑器,但是确实是非常犀利.通过本篇,老司机带你使用VS Code玩转Docker——相信阅读本篇之后,无论是初学者还是老手,都可以非常方便的玩转Docker了!所谓是 ...
- 【CUDA开发】CUDA开发琐碎知识
## 一维矩阵的加 //实现一个一维1*16的小矩阵的加法. //矩阵大小:1*16 //分配一个block,共有16个线程并发. #include <stdio.h> #includ ...
- Andrew Ng机器学习课程10补充
Andrew Ng机器学习课程10补充 VC dimension 讲到了如果通过最小化训练误差,使用一个具有d个参数的hypothesis class进行学习,为了学习好,一般需要参数d的线性关系个训 ...
- Django 之上下文处理器和中间件
一.上下文处理器 上下文处理器是可以返回一些数据,在全局模板中都可以使用.比如登录后的用户信息,在很多页面中都需要使用,那么我们可以放在上下文处理器中,就没有必要在每个视图函数中都返回这个对象. 在s ...