线程私有数据的作用?
线程私有数据的实现?
一 概述:
(1)线程存储器模型:(深入理解计算机系统)
*一组并发线程运行在一个进程的上下文中,每个线程都有自己独立的线程上下文:线程ID、栈、栈指针、程序计数器、条件码和通用目的的寄存器值。每个线程和和其它线程共享进程上下文的剩余部分:整个用户虚拟地址空间(代码、读/写数据、堆、已经所有共享库代码和数据区),也共享同样的打开文件集合。
(2)线程私有数据(Thread-specific data,TSD):存储和查询与某个线程相关数据的一种机制。
*在进程内的所有线程都共享相同的地址空间,即意味着任何声明为静态或外部变量,或在进程堆声明的变量,都可以被进程内所有的线程读写。
*一个线程真正拥有的唯一私有存储是处理器寄存器,栈在“主人”故意暴露给其他线程时也是共享的。
*有时需要提供线程私有数据:可以跨多个函数访问(全局);仅在某个线程有效(私有)。例如:errno。
(3)linux TSD池:(参看源代码)
*不论哪个线程调用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。
(4)线程可以线程的私有数据分配多个键,每个键都可以有一个析构函数与它关联。
二 相关函数:
*在分配线程私有数据之前,需要创建与该数据关联的键。这个键用于获取对线程私有数据的访问权。
(1)pthread_key_create(pthread_key_t *key,void (*destructor)(void *)):创建一个键。
*key:创建的键存放在key指向的内存单元,这个键可以被进程中所有线程使用,但每个线程把这个键与不同的线程私有数据地址进行关联。创建新键时,每个线程的数据地址设为null值。
*destructor:pthread_key_create可以选择为该键关联析构函数。如果destructor参数为null,就说明没有析构函数与键关联。线程退出时,如果其数据地址为非null数值,那么析构函数就会被调用。线程正常退出(pthread_exit或线程执行返回),析构函数会被调用;如果线程调用exit、_exit、_Exit、abort或出现其他非正常退出时,就不会调用析构函数。如果你的线程私有数据地址为堆存储的地址,并且你想要再destructor函数中释放存储,就必须传递给destructor参数,而非调用pthread_getspecific的参数。(详细参考posix多线程程序设计)
*对于每个pthread_key_t变量(即键)必须仅调用一次pthread_key_create。如果一个键创建两次,其实是在创建不同的键,第二个键将覆盖第一个,第一个键与任何线程可能为其设置的值将一起永远的丢失。所以,pthread_key_create放在主函数中执行;或每个线程使用pthread_once来创建键。
(2)一次初始化:一些事情仅仅需要做一次。
pthread_once_t initflag=PTHREAD_ONCE_INIT
pthread_once(pthread_once_t *initflag,void (*initfn)(void))
*initflag必须是一个非局部变量(即全局变量或静态变量),必须初始化为PTHREAD_ONCE_INIT。
*initfn:初始化函数。
*使用pthread_once创建键的格式:
pthread_key_t key;
pthread_once_t initflag=PTHREAD_ONCE_INIT;
void thread_init()
{pthread_key_create(key,destructor);}
int threadfunc()
{
pthread_once(initflag,thread_init);
}
(3)pthread_key_delete(pthread_key_t *key)
*对所有线程都可以调用pthread_key_delete来取消键与线程私有数据之间的关联关系。
*注意:调用pthread_key_delete不激活与该键关联的析构函数。
*POSIX保证一次只能有128个线程私有数据键,因此释放你不再需要的键是有用的。实际支持的键数目由PTHREAD_KEYS_MAX标志的值指定。
(4)关联键和线程私有数据:pthread_setspecific(pthread_key_t key,const void *value);
获取线程私有数据地址:pthread_getspecific(pthread_key_t key);
*如果没有关联私有数据,pthread_getspecific将返回一个空指针,可以据此来判断是否需要调用pthread_setspecific。
*使用pthread_setspecific将线程私有数据键设为NULL,这不是在赋空值,而是说该键在当前的线程中不再有值。