zoukankan      html  css  js  c++  java
  • ZT linux 线程私有数据之 一键多值技术

    这个原作者的这个地方写错了 且他举的例子非常不好。最后有我的修正版本

     pthread_setspecific(key, (void *)&my_errno);

    linux 线程私有数据之一键多值技术TSD池 2012-09-15 16:22:08

    分类: LINUX

                   linux 线程私有数据之 一键多值技术

          进程内的所有线程共享进程的数据空间,因此全局变量为所有线程所共有。但有时线程也需要保存自己的私有数据,这时可以创建线程私有数据(Thread- specific Date)TSD来解决。在线程内部,私有数据可以被各个函数访问,但对其他线程是屏蔽的。一个明显的例子是errno,每个线程都有自己的副本,不然由于线程间的切换,在一个线程里输出的很可能是令一线程的出错信息。
         线程私有数据采用了一种一键多值的技术,即一个键对应多个数值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。使用 线程私有数据时,首先要为每个线程数据创建一个相关联的键。POSIX中操作线程私有数据的主要通过以下4个函数来实现:pthread_key_create(创建一个键),pthread_setspecific(为一个键设置线程私有数据),pthread_getspecific(从一个键读取线程私有数据),pthread_key_delete(删除一个键)。这几个函数的声明如下:

    #include <pthread.h>

       int pthread_key_create(pthread_key_t *key, void (*destr_function) (void*));

       int pthread_key_delete(pthread_key_t key);

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

        void * pthread_getspecific(pthread_key_t key);

       
       pthread_key_create:从Linux的TSD池中分配一项,将其值赋给key供以后访问使用,它的第一个参数key为指向键值的指针,第二个参数为一个函数指针,如果指针不为空,则在线程退出时将以key所关联的数据为参数调用destr_function(),释放分配的缓冲区。
        key一旦被创建,所有线程都可以访问它,但各线程可以根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量,一键多值。一键多值靠的是一个关键数据结构数组,即TSD池其结构如下:

    static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] ={{0,NULL}};

        创建一个TSD就相当于将结构数组中的某一项设置为“in_use”,并将其索引返回给*key,然后设置destructor函数 destr_function。pthread_key_create 创建一个新的线程特定数据Key时,系统搜索其所在进程的 Key 结构数组,找出其中第一个不在使用的元素,并返回该元素的索引键。
        pthread_setspecific:该函数将pointer的值(不是内容)与key相关联。用pthread_setspecific为一个键指定新的线程数据时,线程必须先释放原有的线程数据用以回收空间。
        pthread_getspecific:通过该函数得到与key相关联的数据。
        pthread_key_delete:该函数用来删除一个键,键所占用的内存将被释放。需要注意的是,键占用的内存被释放,与该键关联的线程数据所占用的内存并不被释放。因此,线程数据的释放必须在释放键之前完成。
        下面我们通过一个例子来学习使用这几个函数的使用,功能类似errno在线程的使用,我猜想全局变量errno是在创建一个线程pthread_create
    时利用TSD池为该线程分配一个errno的副本的。

    点击(此处)折叠或打开

    1. /*   my_errno.c
    2.      编译链接:gcc my_errno.c -o my_errno -lpthread
    3. */
    4. // 线程私有数据,一键多值,tsd池
    5. #include <stdio.h>
    6. #include <string.h>
    7. #include <pthread.h>
    8. int my_errno = 0;
    9. pthread_key_t key;
    10. void print_errno(char *str)
    11. {
    12.     printf("%s my_errno:%d ",str, my_errno);
    13. }
    14. void *thread2(void *arg)
    15. {
    16.     printf("thread2 %ld is running ",pthread_self());
    17.     pthread_setspecific(key, (void *)my_errno);
    18.     printf("thread2 %ld returns %d ",pthread_self(),
    19.             pthread_getspecific(key));
    20.     my_errno = 2;    
    21.     print_errno("thread2");
    22. }
    23. void *thread1(void *arg)
    24. {
    25.     pthread_t thid2;
    26.     printf("thread1 %ld is running ",pthread_self());
    27.     pthread_setspecific(key, (void *)my_errno);
    28.     my_errno = 1;
    29.     pthread_create(&thid2, NULL, thread2, NULL);
    30.     sleep(2);
    31.     printf("thread1 %ld returns %d ",pthread_self(),
    32.          pthread_getspecific(key));
    33.    
    34.     print_errno("thread1");
    35. }
    36. void destr(void *arg)
    37. {
    38.     printf("destroy memory ");
    39. }
    40. int main(void)
    41. {
    42.     pthread_t thid1;
    43.  
    44.     printf("main thread begins running. my_errno=%d ",my_errno);
    45.     pthread_key_create(&key, destr);
    46.     pthread_create(&thid1, NULL, thread1, NULL);
    47.     sleep(4);
    48.     pthread_key_delete(key);
    49.     
    50.     printf("main thread exit ");
    51.     return 0;
    52. }

      ./my_errno 后你会发现虽然my_errno是全局的,但在thread1与thread2保留的是私有数据。

    /*   my_errno.c
             编译链接:gcc my_errno.c -o my_errno -lpthread
    */
    
    // 线程私有数据,一键多值,tsd池
    #include <stdio.h>
    #include <string.h>
    #include <pthread.h>
    #include <errno.h>
    
    extern int errno ;
    
    pthread_key_t key;
    
    void print_errno(char *str)
    {
            printf("%s my_errno:%d
    ",str, errno);
    }
    void *thread2(void *arg)
    {
            errno = 2;  
            printf("thread2 %ld is running
    ",pthread_self());
            pthread_setspecific(key, (void *)&errno);
            printf("thread2 %ld returns %d
    ",pthread_self(),
                    pthread_getspecific(key));
              
            print_errno("thread2");
    }
    
    void *thread1(void *arg)
    {
            errno = 1;
            pthread_t thid2;
    
            printf("thread1 %ld is running
    ",pthread_self());
            pthread_setspecific(key, (void *)&errno);
            
            pthread_create(&thid2, NULL, thread2, NULL);
            sleep(2);
            printf("thread1 %ld returns %d
    ",pthread_self(),
                 pthread_getspecific(key));
           
            print_errno("thread1");
    }
    
    void destr(void *arg)
    {
        printf("destroy memory
    ");
    }
    
    int main(void)
    {
        pthread_t thid1;
    
        printf("main thread begins running. my_errno=%d
    ",errno);
        pthread_key_create(&key, destr);
        pthread_create(&thid1, NULL, thread1, NULL);
        sleep(4);
        pthread_key_delete(key);
            
        printf("main thread exit
    ");
    
        return 0;
    }

     UE$ ./1
    main thread begins running. my_errno=0
    thread1 139672638236416 is running
    thread2 139672629843712 is running
    thread2 139672629843712 returns 293377696
    thread2 my_errno:2
    destroy memory



    thread1 139672638236416 returns 301770400
    thread1 my_errno:1
    destroy memory


    main thread exit

  • 相关阅读:
    Aurora 数据库支持多达五个跨区域只读副本
    Amazon RDS 的 Oracle 只读副本
    Amazon EC2 密钥对
    DynamoDB 读取请求单位和写入请求单位
    使用 EBS 优化的实例或 10 Gb 网络实例
    启动 LAMP 堆栈 Web 应用程序
    AWS 中的错误重试和指数退避 Error Retries and Exponential Backoff in AWS
    使用 Amazon S3 阻止公有访问
    路由表 Router Table
    使用MySQLAdmin工具查看QPS
  • 原文地址:https://www.cnblogs.com/jeanschen/p/3499603.html
Copyright © 2011-2022 走看看