线程特定数据 Thread Specified Data

来源:互联网 发布:旅游策划师 知乎 编辑:程序博客网 时间:2024/06/07 13:50
1、为什么需要用到线程特定数据?
    因为在单个线程的程序中,当我们调用同一个函数多次,若其中有静态、全局变量,该变量会随着每一次调用发生变化。
    然而在多线程中,当多个线程同时调用同一个函数,若该函数没有全局、静态变量,那么不会引发错误;否则,我们就需要用到锁来解决问题。
    这里提供的线程特定数据,是使线程函数变为线程安全的一个常用技巧

2、每一个系统支持的线程特定元素限制不一样。POSIX要求这个限制不小于128(对每个进程而言)。系统为每个【进程】维护一个我们称为key结构的结构数组
    key结构中的标志指示某个元素是否正在被使用。
    当一个线程调用pthread_key_create创建一个新的线程特定元素时,系统搜索其key结构数组找出第一个不在使用的元素,返回其索引
    如图:
                                                                
3、进程除了维护一个进程级别的key数组,系统还在进程内维护关于每个特定线程的多条信息。这些特定于线程的结构我们称为Pthread结构
    其中部分内容为称为pkey数组的一个128个元素的指针数组
    如图:

                     


    pkey数组的所有元素被初始化为空指针。这些128个指针是和进程内128个可能的key逐一关联的值

    当我们调用pthread_key_create创建一个键时,系统告诉我们这个键的索引。每个线程随后可以在该键对应下标的pkey数组中存储一个自己的值。



4、那么举个例子,该例子的步骤如下:
    1、一个进程被启动,多个线程被创建
    2、其中一个线程是首个调用函数readline()的线程。进入readline函数后,程序调用pthread_key_create。系统在key数组中找到一个未用的元素后,把它的索引返回给用户,假设是1。
        这里我们将会使用pthread_once函数来确保pthread_key_create只被调用一次,且只被第一个调用pthread_key_create的线程调用。
    3、接着,在readline中,程序调用pthread_getspecific函数获取本线程专属的pkey[1]的值,我们发现它是一个空指针。于是我们调用malloc为我们分配内存空间,并调用pthread_setspecific函数将pkey[1]指向刚刚分配的内存空间,保存本线程特定数据的信息,
    4、另一个线程这是调用readline函数,当然可能刚刚的线程依旧在readline函数中运行。回归到这个新的线程中,我们的readline函数调用pthread_once试图调用pthread__key_create来初始化键,不过既然初始化已经调用过了,它就不再会被调用
    5、readline调用pthread_getspecific发现本线程的pkey[n]是空的,就跟之前线程的处理方式一样,准备去申请空间并保存属于自己线程的特定数据
    6、每个线程都在使用属于自己的数据
    7、在每个线程函数调用结束后,如何释放这些申请的内存空间呢?
        即使用pthread_key_create第二个参数,它指向的就是析构函数(跟C++里的析构一个意思)


以下就是上面步骤中提到的函数:
int pthread_key_create(pthread_key_t *keyptr, void (*destroy)(void *value));int pthread_once(pthread_once_t *onceptr, void (*init)(void));


//功能:本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。pthread_key_t r1_key;pthread_once_t re_once = PTHREAD_ONCE_INIT;//定义析构函数,释放空间void readline_destructor(void *ptr){    free(ptr);}//初始化函数 void readline_once(void){   pthread_key_create(&r1_key,readline_destructor);}ssize_treadline(...){  ....  pthread_once(&r1_once,readline);  if((ptr = pthread_getspecific(r1_key)) == NULL)   {      ptr = malloc(....);      //设置键值对。      pthread_setspecific(r1_key,ptr);       ....    }     .....   }



还有这两个函数:
void *pthread_getspecific(pthread_key_t key);int pthread_setspecific(pthread_key_t key, const void *value);




接下来我给一个自己实验用的实例:
#include "unp.h"pthread_key_t key;void release();void *thread_f1(void *);void *thread_f2(void *);int main(int ac, char *av[]){    pthread_t tid1, tid2;        printf("start to create threads\n");    pthread_key_create(&key,release);        //创建两个实验用线程    pthread_create(&tid1,NULL,&thread_f1,NULL);    pthread_create(&tid2,NULL,&thread_f2,NULL);    sleep(6);    pthread_key_delete(key);    printf("bye\n");    exit(0);}//用定时器来把时间片交错,更能体现特定数据的真实性//线程1void *thread_f1(void *arg){    int tid = pthread_self();    printf("thread %d start\n",tid);    pthread_setspecific(key,(void *)&tid);    sleep(3);    printf("thread %d returns %d\n",tid,*(int *)pthread_getspecific(key));    sleep(2);    return NULL;}//线程2void *thread_f2(void *arg){    int tid = pthread_self();    sleep(1);    printf("thread %d start\n",tid);    pthread_setspecific(key,(void *)&tid);    sleep(1);    printf("thread %d returns %d\n",tid,*(int *)pthread_getspecific(key));    sleep(2);    return NULL;}//析构函数void release(){    printf("this thread specific date is over\n");}



运行结果为:
$ ./TSD start to create threadsthread 1506096896 startthread 1495607040 startthread 1495607040 returns 1495607040thread 1506096896 returns 1506096896this thread specific date is overthis thread specific date is overbye


0 0