在说__thread之前,先来看看pthread_ket_t吧。

  参考:http://blog.csdn.net/lmh12506/article/details/8452700

  上面的博文说的比较通俗易懂。线程私有数据可以理解为线程内的全局变量。在线程内可以被所有函数访问,但是不能被其他线程的函数访问。

  这里博主直接去找pthread.h头文件中的API,发现写的还是很详细的。

  1. /* Functions for handling thread-specific data. */    //用于处理线程特定数据的函数。
  2.  
  3. /* Create a key value identifying a location in the thread-specific      //identifying 识别
  4. data area. Each thread maintains a distinct thread-specific data      //maintains 维护  distinct 不同的
  5. area. DESTR_FUNCTION, if non-NULL, is called with the value
  6. associated to that key when the key is destroyed.                //associated to 关联 即__destr_function如果不是null,则被关联到key销毁的时刻
  7. DESTR_FUNCTION is not called if the value associated is NULL when
  8. the key is destroyed. */
  9. extern int pthread_key_create (pthread_key_t *__key,
  10. void (*__destr_function) (void *))
  11. __THROW __nonnull (());
  12.  
  13. /* Destroy KEY. */
  14. extern int pthread_key_delete (pthread_key_t __key) __THROW;
  15.  
  16. /* Return current value of the thread-specific data slot identified by KEY. */    //slot 槽 这个理解好,把线程局部存储设施比喻为槽。
  17. extern void *pthread_getspecific (pthread_key_t __key) __THROW;              //注意这个返回值,使用时记得把void* 转换成指定的类型
  18.  
  19. /* Store POINTER in the thread-specific data slot identified by KEY. */        //往槽里存储数据
  20. extern int pthread_setspecific (pthread_key_t __key,
  21. const void *__pointer) __THROW ;

  

  贴一段博主自己在电脑上的测试:

  1. #include <pthread.h>
  2. #include <iostream>
  3. #include <vector>
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <unistd.h>
  7. using namespace std;
  8.  
  9. static pthread_key_t pkt;
  10. //FILE* log_handle;
  11.  
  12. void destroy(void *arg)
  13. {
  14. // sleep(10);
  15. fclose((FILE*)arg);    // <1>
  16. }
  17.  
  18. void write_log(const char* log)
  19. {
  20. FILE* log_handle = reinterpret_cast<FILE*> (pthread_getspecific(pkt));  //从槽中拿值
  21. fprintf(log_handle, "%s\n", log);
  22. }
  23.  
  24. void* work(void *arg)
  25. {
  26. FILE *log_handle;
  27. char file_name[128] = "";
  28.  
  29. sprintf(file_name, "/home/ttsj/log/Thread%d.log", static_cast<int>(pthread_self()));
  30.  
  31. cout << file_name << endl;
  32. log_handle = fopen(file_name, "w");
  33.  
  34. pthread_setspecific(pkt, reinterpret_cast<void*> (log_handle));  //向线程私有存储槽中存入值
  35.  
  36. write_log("Thread starting.");
  37.  
  38. //work here...
  39. }
  40.  
  41. int main()
  42. {
  43. vector<pthread_t> savepid;
  44.  
  45. pthread_key_create(&pkt, destroy);  //<2>
  46.  
  47. int i;
  48. for(i = ; i < ; ++i)
  49. {
  50. savepid.push_back(i);
  51. }
  52.  
  53. for(i = ; i < ; ++i)
  54. {
  55. pthread_create(&savepid[i], NULL, work, NULL);  //拉5个线程
  56. }
  57.  
  58. for(i = ; i < ; ++i)
  59. {
  60. cout << "join" << i << endl;
  61. pthread_join(savepid[i], NULL);
  62. }
  63.  
  64. pthread_key_delete(pkt);
  65. sleep();
  66.  
  67. return ;
  68. }

  其实使用没什么难的,但博主却折腾了两个小时,知道为什么吗?请仔细看看<1>和<2>.刚开始就是不知道如何给销毁函数destroy传递参数,而不传递参数,又如何关闭流呢?经过博主两个小时的验证,才搞明白了:看<1>处,得知这个参数根本不用传,系统自动给你传了,而传递的值是什么呢?哈哈,就是线程私有数据!看下面,在程序运行时,destroy成功通过参数关闭了文件流,说明系统确实把线程私有数据(打开的文件流)传递给了pthread_create的第二个参数,而这一切都不用我们自己动手了!

  

  对于pthread_key_t的使用,最好使用RAII:

  1. //博主参照muduo写的
    #include <pthread.h>
  2. #include <vector>
  3. #include <algorithm>
  4. #include <iostream>
  5. #include <boost/checked_delete.hpp>
  6. template <typename T>
  7. class ThreadLocal
  8. {
  9. public:
  10. typedef ThreadLocal<T>* pThreadLocal;  //指向模板的指针,应该用typedef定义一个别名来使用
  11. private:
  12. pthread_key_t pkey;
  13. private:
  14. static void destroy(void *x)
  15. {
  16. T *obj = static_cast <T*> (x);
  17. boost::checked_delete(obj);  //保证可以调用对象的析构函数
  18. }
  19.  
  20. public:
  21. ThreadLocal()  //这些操作都放进构造和析构,自动执行
  22. {
  23. pthread_key_create(&pkey, destroy);
  24. }
  25. ~ThreadLocal()
  26. {
  27. pthread_key_delete(pkey);
  28. }
  29.  
  30. T& value()  //始终保证槽内只有一个T类型对象
  31. {
  32. T* pThreadData = static_cast <T*> (pthread_getspecific(pkey));
  33. if(pThreadData == NULL)
  34. {
  35. T* newData = new T();
  36. pthread_setspecific(pkey, newData);
  37. pThreadData = newData;
  38. }
  39. return *pThreadData;
  40. }
  41. };
  42.  
  43. class base
  44. {
  45. private:
  46. int count = ;
  47. public:
  48. void show()
  49. {
  50. count++;
  51. std::cout << count << std::endl;
  52. }
  53. };
  54.  
  55. void* work(void* args)
  56. {
  57. ThreadLocal<base>::pThreadLocal p = static_cast<ThreadLocal<base>::pThreadLocal>(args);  //p指向线程局部存储设施
  58. base &pb = p->value();  //如果槽内没有对象,则构造一个对象。始终保证槽内存在一个对象。
  59. pb.show();        //使用槽内的对象
  60. }
  61.  
  62. int main()
  63. {
  64. std::vector<pthread_t> vec();
  65. ThreadLocal<base>::pThreadLocal p = new ThreadLocal<base>;  //先构造一个ThreadLocal对象,此对象封装了线程局部存储设施。
  66.  
  67. for(int i = ; i < ; ++i)
  68. pthread_create(&vec[i], NULL, work, static_cast<void*>(p));  //拉两个线程,并把上述对象传递过去
  69. for(int i = ; i < ; ++i)
  70. pthread_join(vec[i], NULL);
  71.  
  72. delete p;
  73. }

输出结果为 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等待多个线程问题:

  1.  for(int i =; i < ; ++i)
  2.  
  3.   pthread_join(pid[i],NULL)
  1. 实际上主线程在pthread_join(1,NULL);这里就挂起了,在等待1号线程结束后再等待2号线程。在等待玩1后,2345线程可能已经结束了,但主线程依然可以回收。
  2.  
  3. checked_delete提升安全性
    依然先贴代码:
  1. //curr.h
  2. class prev;
  3. class curr
  4. {
  5. public:
  6. void Delete(prev* p) {
  7. delete p;    //此处删除,无法调用prev的析构函数,即prev没有被真正销毁。因为curr不知道prev的详细定义。编译器会给出警告,但不会报错。造成内存泄露
  8. }
  9. };
  10. // prev.h
  11. class prev
  12. {
  13. public:
  14. ~prev() {
  15. cout << "delete prev" << endl;
  16. };
  17.  
  18. int main()
  19. {
  20. curr obj;
  21. obj.Delete(new obj);  
  22. }

这里应该可以使用boost的checked_delete(T *p) 进行检查。对不完全类的删除,都应该用这个去删除。当然最好直接用智能指针。贴一下cheched_delete的boost源码:

  1. template<class T> inline void checked_delete(T * x)
  2. {
  3. // intentionally complex - simplification causes regressions
  4. typedef char type_must_be_complete[ sizeof(T)? : - ];  //如果是不完全类型,则[]内是-1,[-1]是不允许的
  5. (void) sizeof(type_must_be_complete);
  6. delete x;
  7. }
  8.  
  9. template<class T> inline void checked_array_delete(T * x)
  10. {
  11. typedef char type_must_be_complete[ sizeof(T)? : - ];
  12. (void) sizeof(type_must_be_complete);
  13. delete [] x;
  14. }
  15.  
  16. template<class T> struct checked_deleter
  17. {
  18. typedef void result_type;
  19. typedef T * argument_type;
  20.  
  21. void operator()(T * x) const
  22. {
  23. // boost:: disables ADL
  24. boost::checked_delete(x);
  25. }
  26. };
  27.  
  28. template<class T> struct checked_array_deleter
  29. {
  30. typedef void result_type;
  31. typedef T * argument_type;
  32.  
  33. void operator()(T * x) const
  34. {
  35. boost::checked_array_delete(x);
  36. }
  37. };
  1. 用法很简单。当然,你得首先安装一下boost库:
  1. #include <boost/checked_delete.hpp>
  2.  
  3. void Delete(prev *p)
  4. {
  5. boost::checked_delete(p);
  6. }

关键字:__thread & pthread_key_t的更多相关文章

  1. Mudo C++网络库第四章学习笔记

    C++多线程系统编程精要 学习多线程编程面临的最大思维方式的转变有两点: 当前线程可能被切换出去, 或者说被抢占(preempt)了; 多线程程序中事件的发生顺序不再有全局统一的先后关系; 当线程被切 ...

  2. 线程局部存储tls的使用

    线程局部存储(Thread Local Storage,TLS)主要用于在多线程中,存储和维护一些线程相关的数据,存储的数据会被关联到当前线程中去,并不需要锁来维护.. 因此也没有多线程间资源竞争问题 ...

  3. 作为一个新手的Oracle(DBA)学习笔记【转】

    一.Oracle的使用 1).启动 *DQL:数据查询语言 *DML:数据操作语言 *DDL:数据定义语言 DCL:数据控制语言 TPL:事务处理语言 CCL:指针控制语言 1.登录 Win+R—cm ...

  4. gcc 中__thread 关键字的示例代码

    __thread 关键字的解释:   Thread Local Storage  线程局部存储(tls)是一种机制,通过这一机制分配的变量,每个当前线程有一个该变量的实例. gcc用于实现tls的运行 ...

  5. 线程局部存储空间 pthread_key_t、__thread 即 ThreadLocal

    https://www.jianshu.com/p/495ea7ce649b?utm_source=oschina-app 该博客还未学习完  还有   pthread_key_t    Thread ...

  6. Qt:用 __thread 关键字让每个线程有自己的全局变量

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/wsj18808050/article/d ...

  7. __thread关键字[转]

    自 http://blog.csdn.net/liuxuejiang158blog/article/details/14100897 __thread是GCC内置的线程局部存储设施,存取效率可以和全局 ...

  8. [转] __thread关键字

    http://blog.csdn.net/liuxuejiang158blog/article/details/14100897 __thread是GCC内置的线程局部存储设施,存取效率可以和全局变量 ...

  9. 多线程编程--- __thread关键字

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

随机推荐

  1. Flutter 中的路由

    Flutter 中的路由通俗的讲就是页面跳转.在 Flutter 中通过 Navigator 组件管理路由导航. 并提供了管理堆栈的方法.如:Navigator.push 和 Navigator.po ...

  2. ionic4 路由跳转、ionic4 路由跳转传值 NavController 返回上一页 、NavController 回到根

    1.普通路由跳转 <ion-button [routerLink]="['/pinfo']"> 跳转到详情 </ion-button> <ion-he ...

  3. Mapbox显示地图案例

    mapbox地图入门案例 <!DOCTYPE html> <html> <head> <meta charset='utf-8' /> <titl ...

  4. gfs下载文件较大,可以分区域分变量下载

       一.下载 所有字段的GFS预报(大致有325个字段),1度的文件有1G多,0.5度的3.5G左右. 若每天下载0.6.12.18四个发布点的数据,那是很费时费力的.而且经常会被IDS/IPS设备 ...

  5. npm publish 一直报错 404

    在封装 zswui  react ui 组件库的时候,尝试了下 github的 packages 包设置,然后就给自己挖坑了. zswui   这是一个从零开始配置,实现组件开发测试的项目. 因为设置 ...

  6. 基于libuv的TCP设计(三)

      基于libuv的TCP设计(一) 基于libuv的TCP设计(二)   一.第二版本的libuv_tcp已经基本可以使用.不会出错与崩溃现象,支持几百路客户端同时连接.可是有一缺陷就占用CPU非常 ...

  7. 高级UI-自定义动画框架

    有的时候会需要做一些自定义的动画效果,在会反复用到的动画效果可以考虑做成动画框架,方便使用,做成框架的话就需要考虑很多的问题,最典型的问题就是属性和方法必须要是可配置的,这里就来聊一聊自定义动画框架的 ...

  8. C# 人工智能开源库生物特征

    C# 人工智能开源库生物特征 Machine learning made in a minute http://accord-framework.net/ Accord.NET是AForge.NET框 ...

  9. visual studio 2017搭建linux c++开发环境

    https://blog.csdn.net/cekonghyj/article/details/77917433 https://blog.csdn.net/norsd/article/details ...

  10. 解决 IE 或者兼容模式不支持 document.getElementsByClassName() 的方法

    网页错误详细信息消息: 对象不支持此属性或方法 document.getElementsByClassName('element_name') 需要自己实现下该方法,因为ie5之前的版本并不支持这个方 ...