计算机基础--线程私有变量
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。
线程拥有自己的全局变量,这种特殊的变量仅在某个线程内部有效。线程的私有数据可以被其内部的所有函数访问,但是对其他线程是屏蔽的。线程私有数据采用了一种称为一键多值的技术,即一个键对应多个数值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。
操作线程私有数据的函数主要有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。
相关文章推荐