• 多线程私有数据pthread_key_create


    在多线程的环境下,进程内的所有线程共享进程的数据空间。因此全局变量为所有线程共享。在程序设计中有时需要保存线程自己的全局变量,这种特殊的变量仅在线程内部有效。

    如常见的errno,它返回标准的错误码。errno不应该是一个局部变量。几乎每个函数都应该可以访问他,但他又不能作为是一个全局变量。否则在一个线程里输出的很可能是另一个线程的

    出错信息,这个问题可以通过创建线程的私有数据(TSD  thread specific data)来解决。在线程内部,私有数据可以被各个函数访问。但他对其他线程是屏蔽的。
     

    线程私有数据采用了一键多值的技术,即一个键对应多个值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。

    int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,

    如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。

    key一旦被创建,所有线程都可以访问它,但各线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量,一键多值。

    一键多值靠的是一个关键数据结构数组即TSD池,创建一个TSD就相当于将结构数组中的某一项设置为“in_use”,并将其索引返回给*key,然后设置清理函数。

    1、创建一个键

    2、为一个键设置线程私有数据

    3、从一个键读取线程私有数据void *pthread_getspecific(pthread_key_t key);

    4、线程退出(退出时,会调用destructor释放分配的缓存,参数是key所关联的数据)

    5、删除一个键

    int pthread_setspecific(pthread_key_t key,const void *pointer));
    void *pthread_getspecific(pthread_key_t key);

    set是把一个变量的地址告诉key,一般放在变量定义之后,get会把这个地址读出来,然后你自己转义成相应的类型再去操作,注意变量的有效期。
    只不过,在不同的线程里可以操作同一个key,他们不会冲突,比如线程a,b,c set同样的key,分别get得到的地址会是之前各自传进去的值。
    这样做的意义在于,可以写一份线程代码,通过key的方式多线程操作不同的数据。

    int pthread_setspecific(pthread_key_t key, const void *value);该函数将value的值(不是内容)与key相关联。用pthread_setspecific为一个键指定新的线程数据时,线程必须先释放原有的线程数据用以回收空间。

    nt pthread_key_delete(pthread_key_t key);用来删除一个键,删除后,键所占用的内存将被释放。注销一个TSD,这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数(destr_function),

    而只是将TSD释放以供下一次调用pthread_key_create()使用。需要注意的是,键占用的内存被释放。与该键关联的线程数据所占用的内存并不被释放。因此,线程数据的释放,必须在释放键之前完成。

    简单的示例代码:

    1. #include
    2. #include
    3. pthread_key_t key;
    4. pthread_t thid1;
    5. pthread_t thid2;
    6. void* thread2(void* arg)
    7. {
    8. printf("thread:%lu is running\n", pthread_self());
    9. int key_va = 3 ;
    10. pthread_setspecific(key, (void*)key_va);
    11. printf("thread:%lu return %d\n", pthread_self(), (int)pthread_getspecific(key));
    12. }
    13. void* thread1(void* arg)
    14. {
    15. printf("thread:%lu is running\n", pthread_self());
    16. int key_va = 5;
    17. pthread_setspecific(key, (void*)key_va);
    18. pthread_create(&thid2, NULL, thread2, NULL);
    19. printf("thread:%lu return %d\n", pthread_self(), (int)pthread_getspecific(key));
    20. }
    21. int main()
    22. {
    23. printf("main thread:%lu is running\n", pthread_self());
    24. pthread_key_create(&key, NULL);
    25. pthread_create(&thid1, NULL, thread1, NULL);
    26. pthread_join(thid1, NULL);
    27. pthread_join(thid2, NULL);
    28. int key_va = 1;
    29. pthread_setspecific(key, (void*)key_va);
    30. printf("thread:%lu return %d\n", pthread_self(), (int)pthread_getspecific(key));
    31. pthread_key_delete(key);
    32. printf("main thread exit\n");
    33. return 0;
    34. }

    释放空间、每次设置之前判断的代码:

    1. /*三个线程:主线程,th1,th2各自有自己的私有数据区域
    2. */
    3. #include
    4. #include
    5. #include
    6. #include
    7. static pthread_key_t str_key;
    8. //define a static variable that only be allocated once
    9. static pthread_once_t str_alloc_key_once=PTHREAD_ONCE_INIT;
    10. static void str_alloc_key();
    11. static void str_alloc_destroy_accu(void* accu);
    12. char* str_accumulate(const char* s)
    13. { char* accu;
    14. pthread_once(&str_alloc_key_once,str_alloc_key);//str_alloc_key()这个函数只调用一次
    15. accu=(char*)pthread_getspecific(str_key);//取得该线程对应的关键字所关联的私有数据空间首址
    16. if(accu==NULL)//每个新刚创建的线程这个值一定是NULL(没有指向任何已分配的数据空间)
    17. { accu=malloc(1024);//用上面取得的值指向新分配的空间
    18. if(accu==NULL) return NULL;
    19. accu[0]=0;//为后面strcat()作准备
    20. pthread_setspecific(str_key,(void*)accu);//设置该线程对应的关键字关联的私有数据空间
    21. printf("Thread %lx: allocating buffer at %p\n",pthread_self(),accu);
    22. }
    23. strcat(accu,s);
    24. return accu;
    25. }
    26. //设置私有数据空间的释放内存函数
    27. static void str_alloc_key()
    28. { pthread_key_create(&str_key,str_alloc_destroy_accu);/*创建关键字及其对应的内存释放函数,当进程创建关键字后,这个关键字是NULL。之后每创建一个线程os都会分给一个对应的关键字,关键字关联线程私有数据空间首址,初始化时是NULL*/
    29. printf("Thread %lx: allocated key %d\n",pthread_self(),str_key);
    30. }
    31. /*线程退出时释放私有数据空间,注意主线程必须调用pthread_exit()(调用exit()不行)才能执行该函数释放accu指向的空间*/
    32. static void str_alloc_destroy_accu(void* accu)
    33. { printf("Thread %lx: freeing buffer at %p\n",pthread_self(),accu);
    34. free(accu);
    35. }
    36. //线程入口函数
    37. void* process(void *arg)
    38. { char* res;
    39. res=str_accumulate("Resule of ");
    40. if(strcmp((char*)arg,"first")==0)
    41. sleep(3);
    42. res=str_accumulate((char*)arg);
    43. res=str_accumulate(" thread");
    44. printf("Thread %lx: \"%s\"\n",pthread_self(),res);
    45. return NULL;
    46. }
    47. //主线程函数
    48. int main(int argc,char* argv[])
    49. { char* res;
    50. pthread_t th1,th2;
    51. res=str_accumulate("Result of ");
    52. pthread_create(&th1,NULL,process,(void*)"first");
    53. pthread_create(&th2,NULL,process,(void*)"second");
    54. res=str_accumulate("initial thread");
    55. printf("Thread %lx: \"%s\"\n",pthread_self(),res);
    56. pthread_join(th1,NULL);
    57. pthread_join(th2,NULL);
    58. pthread_exit(0);
    59. }

    多线程私有数据pthread_key_create - 邶风 - 博客园

  • 相关阅读:
    【算法刷题日记之本手篇】杨辉三角的变形与二叉树的镜像
    arthas调查内存溢出 kibana宕机导致内存溢出
    初探亚马逊 AI 编程助手 CodeWhisperer
    BP神经网络算法基本原理,bp神经网络算法的原理
    Prism 2.4.1 在VS里的奇怪报错
    Vue--》简易资金管理系统后台项目实战(前端)
    webpack面试题
    HDLBis-Fsm3s
    Eureka注册中心以及Ribbon负载均衡
    C++问答2 三大特性
  • 原文地址:https://blog.csdn.net/qixiang2013/article/details/126126112