您的位置:首页 > 理论基础

计算机基础--线程私有变量

2014-08-12 13:51 344 查看
多线程环境下的私有数据(Thread-specific Data, TSD):

    线程拥有自己的全局变量,这种特殊的变量仅在某个线程内部有效。线程的私有数据可以被其内部的所有函数访问,但是对其他线程是屏蔽的。线程私有数据采用了一种称为一键多值的技术,即一个键对应多个数值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。

    操作线程私有数据的函数主要有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_setspecific(pthread_key_t key, const void * pointer);

        void* pthread_getspecific(pthread_key_t key);

        int pthread_key_delete(pthread_key_t key);

一.  概念及作用
在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据。在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共  有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问,比如程序可能需要每个线程维护一个链表,而使用相  同的函数操作,最简单的办法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由 Posix 线程库维护,称为线程私有数据  (Thread-specific Data,或 TSD)。

二.  创建和注销

Posix 定义了两个 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()使用。在 LinuxThreads 中,它还会将与之相关的线程数据项设为 NULL(见"访问")。

三.  访问

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 值经过类似计算得到数据所在位置索引,再取出其中内容返回。

四. 使用范例

以下这个例子说明如何使用这一机制达到存储线程私有数据的目的。

[cpp]
view plaincopy

#include <stdio.h>   
#include <pthread.h>   
  
pthread_key_t key;   
  
void echomsg(int t)   
{   
printf("destructor excuted in thread %d,param=%d\n",pthread_self(),t);   
}   
  
void * child1(void *arg)   
{   
int tid=pthread_self();   
printf("thread %d enter\n",tid);   
pthread_setspecific(key,(void *)tid);   
sleep(2);   
printf("thread %d returns %d\n",tid,pthread_getspecific(key));   
sleep(5);   
}   
  
void * child2(void *arg)   
{   
int tid=pthread_self();   
printf("thread %d enter\n",tid);   
pthread_setspecific(key,(void *)tid);   
sleep(1);   
printf("thread %d returns %d\n",tid,pthread_getspecific(key));   
sleep(5);   
}   
  
int main(void)   
{   
int tid1,tid2;   
  
printf("hello\n");   
pthread_key_create(&key,echomsg);   
pthread_create(&tid1,NULL,child1,NULL);   
pthread_create(&tid2,NULL,child2,NULL);   
sleep(10);   
pthread_key_delete(key);   
printf("main thread exit\n");   
return 0;   
}   

运行结果如下:

hello

thread 1082350784 enter

thread 1090739264 enter

thread 1090739264 returns 1090739264

thread 1082350784 returns 1082350784

main thread exit

给例程创建两个线程分别设置同一个线程私有数据为自己的线程ID,为了检验其私有性,程序错开了两个线程私有数据的写入和读出的时间,从程序运行结果可以看出,两个线程对TSD的修改互不干扰。同时,当线程退出时,清理函数会自动执行,参数为tid。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: