zoukankan      html  css  js  c++  java
  • 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 后的进程中便能体现出来。

  • 相关阅读:
    sp2010 升级sp2013 用户无法打开网站
    powerviot install in sharepoint 2013
    can not connect cube in performancce dashboard
    westrac server security configure user info
    添加报表服务在多服务器场
    sharepoint 2013 office web app 2013 文档在线浏览 IE11 浏览器不兼容解决方法
    delete job definition
    目前付款申请单内网打开慢的问题
    item style edit in sharepoint 2013
    Could not load file or assembly '$SharePoint.Project.AssemblyFullName$'
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332842.html
Copyright © 2011-2022 走看看