在多线程环境下,进程内的所有线程共享进程的数据空间,因此全局变量为所有线程共有。在程序设计中有时需要保存线程自己的全局变量,这种特殊的变量仅在某个线程内部有效。如常见的变量errno,它返回标准的出错代码。errno不应该是一个局部变量,几乎每个函数都应该可以访问它;但它又不能作为是一个全局变量,否则在一个线程 里输出的很可能是另一个线程的出错信息,这个问题可以通过创建线程的私有数据(Thread-specific Data,或TSD)来解决。
在线程内部,线程私有数据可以被各个函数访问,但它对其他线程是屏蔽的。线程私有数据采用了一种被称为一键多值的技术,即一个键对应多个数值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。使用线程私有数据时,首先要为每个线程数据创建一个相关联的键。在各个线程内部,都使用这个公用的键来指代线程数据,但是在不同的线程中这个键代表的数据是不同的。操作线程私有数据的函数主要有4个:
pthread_key_create (创建一个键), pthread_setspecific(为一个键 设置线程私有数据),pthread_getspecifie(从一个键读取线程私有数据)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);
pthread_key_create
:
- 目的:用于创建一个线程特定数据的键。
- 参数:
key
:一个指向pthread_key_t
变量的指针,用于存储键。destr_function
:一个指向析构函数的指针,在线程退出时会调用该函数,并以key所关联的数据作为参数。
pthread_setspecific
:
- 目的:用于将线程特定值与键关联起来。
- 参数:
key
:之前使用pthread_key_create
创建的键。pointer
:要与当前线程的键关联的数据的指针。
pthread_getspecific
:
- 目的:检索与给定键关联的线程特定值。
- 参数:
key
:要检索当前线程的键关联的线程特定值。- 返回值:指向与键关联的线程特定数据的指针,用于当前线程。
pthread_key_delete
:
- 目的:用于删除线程特定数据的键。
- 参数:
key
:使用pthread_key_create
创建的要删除的键。- 返回值:成功时返回0,如果发生错误则返回错误代码。
- 删除后,键所占用的内存被释放,与键关联的线程数据所占用的内存并不被释放。所以,要记得先释放线程数据。
可以这么理解,键值是对所有线程都可见的全局变量,但是不同线程可以让同一键值关联到线程自身的数据,从而实现不同线程通过同一全局的键值得到不同数据(通过getspecific获得)。
书上示例程序运行起来也是问题重重,我也改了一下
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
pthread_key_t key;
void *thread2(void *arg){
int tsd=5;
printf("thread %d is running\n",pthread_self());
pthread_setspecific(key,(const void *)&tsd);//线程2给key关联到线程2的tsd
printf("thread %d returns %d\n",pthread_self(),*(int *)pthread_getspecific(key));
return nullptr;
}
void *thread1(void *arg){
int tsd=0;
pthread_t thid2;//线程1又创建了一个线程2
printf("thread %d is running\n",pthread_self());
pthread_setspecific(key,(const void *)&tsd);//这是线程1里面给key关联到线程1的tsd
pthread_create(&thid2,nullptr,thread2,nullptr);
sleep(5);
printf("thread %d returns %d\n",pthread_self(),*(int *)pthread_getspecific(key));
return nullptr;
}
int main(){
pthread_t thid1;//线程1
printf("main thread begins running \n");
pthread_key_create(&key,nullptr);//创建键值,没有清理函数所以第二个参数给了null
pthread_create(&thid1,nullptr,thread1,nullptr);
sleep(10);
pthread_key_delete(key);
printf("main thread exit\n");
return 0;
}
运行结果如图,可以看到不同的线程对key执行getspecific会得到不同的值