• posix多线程有感线程高级编程(pthread_key_t)


           下面说一下线程中特有的线程存储,Thread Specific Data 。线程存储有什么用了?他是什么意思了?大家都知道,在多线程程序中,所有线程共享程序中的变量。现在有一全局变量,所有线程都可以使用它,改变它的值。而如果每个线程希望能单独拥有它,那么就需要使用线程存储了。表面上看起来这是一个全局变量,所有线程都可以使用它,而它的值在每一个线程中又是单独存储的。这就是线程存储的意义。

           线程私有数据采用了一种被称为一键多值的技术,即一个键对应多个数值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。使用线程私有数据时,首先要为每个线程数据创建一个相关联的键。在各个线程内部,都使用这个公用的键来指代线程数据,但是在不同的线程中,这个键代表的数据是不同的。操作线程私有数据的函数主要有4个:pthread_key_create(创建一个键),pthread_setspecific(为一个键设置线程私有数据),pthread_getspecific(从一个键读取线程私有数据),pthread_key_delete(删除一个键)。
          下面说一下线程存储的具体用法:
    1. 创建一个类型为 pthread_key_t 类型的变量。

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

    3. 当线程中需要存储特殊值的时候,可以调用 pthread_setspcific() 。该函数有两个参数,第一个为前面声明的 pthread_key_t 变量,第二个为 void* 变量,这样你可以存储任何类型的值。函数将value的值(不是内容)与key相关联。用pthread_setspecific为一个键指定新的线程数据时,线程必须先释放原有的线程数据用以回收空间。

    4. 如果需要取出所存储的值,调用 pthread_getspecific() 。该函数的参数为前面提到的 pthread_key_t 变量,该函数返回 void * 类型的值。

    5.pthread_key_delete:该函数用来删除一个键,键所占用的内存将被释放。需要注意的是,键占用的内存被释放,与该键关联的线程数据所占用的内存并不被释放。因此,线程数据的释放必须在释放键之前完成。
      下面是前面提到的函数的原型:

    pthread_key_t key;
    int pthread_create(pthread_key_t,void(*destructor)(void *));
    int pthread_key_delete(pthread_key_t key);
    int pthread_setspecific(pthread_key_t key,const void *value);
    void* pthread_getspecific(pthread_key_t key);
    /*
     * tsd_destructor.c
     *
     * Demonstrate use of thread-specific data destructors.
     */
    #include <pthread.h>
    #include "errors.h"
    
    /*
     * Structure used as value of thread-specific data key.
     */
    typedef struct private_tag {
        pthread_t   thread_id;
        char        *string;
    } private_t;
    
    pthread_key_t identity_key;         /* Thread-specific data key */
    pthread_mutex_t identity_key_mutex = PTHREAD_MUTEX_INITIALIZER;
    long identity_key_counter = 0;
    
    /*
     * This routine is called as each thread terminates with a value
     * for the thread-specific data key. It keeps track of how many
     * threads still have values, and deletes the key when there are
     * no more references.
     */
    void identity_key_destructor (void *value)
    {
        private_t *private = (private_t*)value;
        int status;
    
        printf ("thread \"%s\" exiting...\n", private->string);
        free (value);
        status = pthread_mutex_lock (&identity_key_mutex);
        if (status != 0)
            err_abort (status, "Lock key mutex");
        identity_key_counter--;
        if (identity_key_counter <= 0) {
            status = pthread_key_delete (identity_key);
            if (status != 0)
                err_abort (status, "Delete key");
            printf ("key deleted...\n");
        }
        status = pthread_mutex_unlock (&identity_key_mutex);
        if (status != 0)
            err_abort (status, "Unlock key mutex");
    }
    
    /*
     * Helper routine to allocate a new value for thread-specific
     * data key if the thread doesn't already have one.
     */
    void *identity_key_get (void)
    {
        void *value;
        int status;
    
        value = pthread_getspecific (identity_key);
        if (value == NULL) {
            value = malloc (sizeof (private_t));
            if (value == NULL)
                errno_abort ("Allocate key value");
            status = pthread_setspecific (identity_key, (void*)value);
            if (status != 0)
                err_abort (status, "Set TSD");
        }
        return value;
    }
    
    /*
     * Thread start routine to use thread-specific data.
     */
    void *thread_routine (void *arg)
    {
        private_t *value;
    
        value = (private_t*)identity_key_get ();
        value->thread_id = pthread_self ();
        value->string = (char*)arg;
        printf ("thread \"%s\" starting...\n", value->string);
        sleep (2);
        return NULL;    
    }
    
    void main (int argc, char *argv[])
    {
        pthread_t thread_1, thread_2;
        private_t *value;
        int status;
    
        /*
         * Create the TSD key, and set the reference counter to
         * the number of threads that will use it (two thread_routine
         * threads plus main). This must be done before creating
         * the threads! Otherwise, if one thread runs the key's
         * destructor before any other thread uses the key, it will
         * be deleted.
         *
         * Note that there's rarely any good reason to delete a
         * thread-specific data key.
         */
        status = pthread_key_create (&identity_key, identity_key_destructor);
        if (status != 0)
            err_abort (status, "Create key");
        identity_key_counter = 3;
        value = (private_t*)identity_key_get ();
        value->thread_id = pthread_self ();
        value->string = "Main thread";
        status = pthread_create (&thread_1, NULL,
            thread_routine, "Thread 1");
        if (status != 0)
            err_abort (status, "Create thread 1");
        status = pthread_create (&thread_2, NULL,
            thread_routine, "Thread 2");
        if (status != 0)
            err_abort (status, "Create thread 2");
        pthread_exit (NULL);
    }

    最后说一下线程的本质:
    其实在Linux 中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone() 。该系统copy 了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy 过程和fork 不一样。copy 后的进程和原先的进程共享了所有的变量,运行环境(clone的实现是可以指定新进程与老进程之间的共享关系,100%共享就表示创建了一个线程)。这样,原先进程中的变量变动copy 后的进程中便能体现出来。

  • 相关阅读:
    尚硅谷SpringBoot
    try-with-resources
    Spring中的InitializingBean接口
    @Deprecated注解
    OpenStack 九大组建介绍
    rainbow 实现云上迁移
    推送一个私有镜像到harbor
    搭建企业级私有registry Harbor
    Linux 部署 jenkins
    OpenStack 发放虚拟机流程
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6173130.html
Copyright © 2020-2023  润新知