1. 线程特定数据是什么?
单线程 C 程序有两类基本数据:局部数据和全局数据。对于多线程 C 程序,添加了第三类
数据:线程特定数据。线程特定数据与全局数据非常相似,区别在于前者为线程专有。
在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据, 然而在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问。POSIX线程库通过维护一定的数据结构来解决这个问题,这个些数据称为(Thread-specific-data或 TSD)。
线程特定数据基于每线程进行维护。TSD(特定于线程的数据)是定义和引用线程专用数
据的唯一方法。每个线程特定数据项都与一个作用于进程内所有线程的键关联。通过使用
key,线程可以访问基于每线程进行维护的指针 (void *)。
从上图可知:当调用pthread_key_create 后会产生一个所有线程都可见的线程特定数据(TSD)的键值(如上图中所有的线程都会得到一个pkey[1]的值), 但是这个键所指向的真实数据却是不同的,虽然都是pkey[1], 但是他们并不是指向同一块内存,而是指向了只属于自己的实际数据, 因此, 如果线程0更改了pkey[1]所指向的数据, 而并不能够影像到线程n;
在线程调用pthread_setspecific后会将每个线程的特定数据与thread_key_t绑定起来,虽然只有一个pthread_key_t,但每个线程的特定数据是独立的内存空间,当线程退出时会执行destructor 函数。
2. 常用函数有哪些?
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));
//分配用于标识进程中线程特定数据的键。键对进程中的所有线程来说是全局的。
int pthread_key_delete(pthread_key_t key);
//删除线程特定数据键
int pthread_setspecific(pthread_key_t key, const void *pointer);
//设置线程特定数据
void * pthread_getspecific(pthread_key_t key);
//获取线程特定数据
3. 示例
/** 示例1: 设置/获取线程特定数据
在两个线程中分别设置/获取线程特定数据, 查看两个线程中的数据是否是一样的(肯定是不一样的O(∩_∩)O~)
**/
pthread_key_t key;
typedef struct Tsd
{
pthread_t tid;
char *str;
} tsd_t;
//用来销毁每个线程所指向的实际数据
void destructor_function(void *value)
{
free(value);
cout << "destructor ..." << endl;
}
void *thread_routine(void *args)
{
//设置线程特定数据
tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
value->tid = pthread_self();
value->str = (char *)args;
pthread_setspecific(key, value);
printf("%s setspecific, address: %p\n", (char *)args, value);
//获取线程特定数据
value = (tsd_t *)pthread_getspecific(key);
printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
sleep(2);
//再次获取线程特定数据
value = (tsd_t *)pthread_getspecific(key);
printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
pthread_exit(NULL);
}
int main()
{
//这样每个线程当中都会有一个key可用了,
//但是每个key所绑定的实际区域需要每个线程自己指定
pthread_key_create(&key, destructor_function);
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, thread_routine, (void *)"thread1");
pthread_create(&tid2, NULL, thread_routine, (void *)"thread2");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_key_delete(key);
return 0;
}
/** 示例2:运用pthread_once, 让key只初始化一次
注意: 将对key的初始化放入到init_routine中
**/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
pthread_key_t key;
pthread_once_t once_control = PTHREAD_ONCE_INIT;
typedef struct Tsd
{
pthread_t tid;
char *str;
} tsd_t;
//线程特定数据销毁函数,
//用来销毁每个线程所指向的实际数据
void destructor_function(void *value)
{
free(value);
printf("destructor ...\n");
}
//初始化函数, 将对key的初始化放入该函数中,
//可以保证inti_routine函数只运行一次
void init_routine()
{
pthread_key_create(&key, destructor_function);
printf("init...\n");
}
void *thread_routine(void *args)
{
pthread_once(&once_control, init_routine);
//设置线程特定数据
tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
value->tid = pthread_self();
value->str = (char *)args;
pthread_setspecific(key, value);
printf("%s setspecific, address: %p\n", (char *)args, value);
//获取线程特定数据
value = (tsd_t *)pthread_getspecific(key);
printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
sleep(2);
//再次获取线程特定数据
value = (tsd_t *)pthread_getspecific(key);
printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
pthread_exit(NULL);
}
int main()
{
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, thread_routine, (void *)"thread1");
pthread_create(&tid2, NULL, thread_routine, (void *)"thread2");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_key_delete(key);
return 0;
}
执行结果如下所示:
junior@ubuntu:~/thread$ ./thread_spec
init...
thread2 setspecific, address: 0x7f2584000f30
tid: 0x8971a700, str = thread2
thread1 setspecific, address: 0x7f257c000b20
tid: 0x89f1b700, str = thread1
tid: 0x8971a700, str = thread2
destructor ...
tid: 0x89f1b700, str = thread1
destructor ...
网友评论