zoukankan      html  css  js  c++  java
  • 浅谈POSIX线程的私有数据

          当线程中的一个函数需要创建私有数据时,该私有数据在对函数的调用之间保持一致,数据能静态地分配在存储器中,当我们采用命名范围也许可以实现它使用在函数或是文件(静态),或是全局(EXTERN)。但是当涉及到线程时就不是那么简单了。在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据。在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问,比如程序可能需要每个线程维护一个链表,而使用相同的函数操作,最简单的办法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由Posix线程库维护,称为线程私有数据(Thread-specific Data,或TSD)。

         线程私有数据允许每一个线程保有一份变量的拷贝,每个线程有一串通过共有的“键”值索引的线程私有数据,键对于每个线程都是相同的,但是每个线程都能将它独立的键值与共享键联系,每个线程能在任意时间为键改变它的私有值,而不会影响键或任意外线程拥有的键值。

        在POSIX线程中,这里需要的键值即为创建一个类型为 pthread_key_t 类型的变量。   并定义下面的API来创建TSD:

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

     该函数从TSD池中分配一项,将其值赋给key供以后访问使用。如果destr_function不为空,在线程退出(pthread_exit())时将以key所关联的数据为参数调用destr_function(),以释放分配的缓冲区。

    不论哪个线程调用pthread_key_create(),所创建的key都是所有线程可访问的,但各个线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量。在LinuxThreads的实现中,TSD池用一个结构数组表示:

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


    创建一个TSD就相当于将结构数组中的某一项设置为"in_use",并将其索引返回给*key,然后设置destructor函数为destr_function。

    销一个TSD采用如下API:

    int pthread_key_delete(pthread_key_t key)

    这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数(destr_function),而只是将TSD释放以供下一次调用pthread_key_create()使用。这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数(destr_function),而只是将TSD释放以供下一次调用pthread_key_create()使用.

    TSD的读写都通过专门的Posix Thread函数进行,其API定义如下:

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

    写入(pthread_setspecific())时,将pointer的值(不是所指的内容)与key相关联,而相应的读出函数则将与key相关联的数据读出来。数据类型都设为void *,因此可以指向任何类型的数据。

    在LinuxThreads中,使用了一个位于线程描述结构(_pthread_descr_struct)中的二维void *指针数组来存放与key关联的数据,数组大小由以下几个宏来说明:

    #define PTHREAD_KEY_2NDLEVEL_SIZE       32
    #define PTHREAD_KEY_1STLEVEL_SIZE   
    ((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1)
    / PTHREAD_KEY_2NDLEVEL_SIZE)


    其中在/usr/include/bits/local_lim.h中定义了PTHREAD_KEYS_MAX为1024,     因此一维数组大小为32。而具体存放的位置由key值经过以下计算得到:

    idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE
    idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE

    也就是说,数据存放与一个32×32的稀疏矩阵中。同样,访问的时候也由key值经过类似计算得到数据所在位置索引,再取出其中内容返回。

    下面看看怎么去使用这个线程私有数据(TSD),就以我自己写的这个为例吧,

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    pthread_key_t key; //初始定义线程私有数据键
    
    typedef
    struct tsd_tag{      //自定义数据类型
    	pthread_t thread_tg;
    	char *string;
    }tsd_t;
    
    void destructor (tsd_t *value_pointer) {   //线程结束释放资源
        printf ("thread %lu ends, param = %s
    ", value_pointer->thread_tg,value_pointer->string);
        free (value_pointer);
        value_pointer = NULL;
    }
    void * thread_routine(void *arg) {      //线程数据处理函数
        tsd_t *value_pointer = (tsd_t *)malloc(sizeof(tsd_t));
        value_pointer->string=(char *)arg;
        value_pointer->thread_tg=pthread_self();
        printf ("thread %lu is running
    ", value_pointer->thread_tg);
        pthread_setspecific (key, (void *) value_pointer);
        sleep (3);
        printf ("thread %lu returns %s
    ", value_pointer->thread_tg,((tsd_t *)pthread_getspecific(key))->string);
        sleep (4);
    }
    int main(int argc , char *argv[]) {
        pthread_t thid1, thid2;
    
        printf ("Main Thread begins running
    ");
        pthread_key_create (&key, (void *)destructor);
        pthread_create (&thid1, NULL, thread_routine, "Thread 1");
        pthread_create (&thid2, NULL, thread_routine, "Thread 2");
        sleep (8);
        pthread_key_delete (key); //删除线程私有数据键
        printf ("Main Thread exit
    ");
        return 0;
    }


    这段代码初始时创建了两个线程分别是Thread1 and Thread2 ,但都是调用了相同的函数thread_routine(),通过线程私有数据的键值关联,保证了,两个线程分别对同一个数据类型 tsd_t 的数据进行了私有化处理,达到了不同的数据处理结果。

    下面是程序的运行结果:









  • 相关阅读:
    5 Things Every Manager Should Know about Microsoft SharePoint 关于微软SharePoint每个经理应该知道的五件事
    Microsoft SharePoint 2010, is it a true Document Management System? 微软SharePoint 2010,它是真正的文档管理系统吗?
    You think you use SharePoint but you really don't 你认为你使用了SharePoint,但是实际上不是
    Introducing Document Management in SharePoint 2010 介绍SharePoint 2010中的文档管理
    Creating Your Own Document Management System With SharePoint 使用SharePoint创建你自己的文档管理系统
    MVP模式介绍
    权重初始化的选择
    机器学习中线性模型和非线性的区别
    神经网络激励函数的作用是什么
    深度学习中,交叉熵损失函数为什么优于均方差损失函数
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3209214.html
Copyright © 2011-2022 走看看