进程中的所有线程都可以访问进程的整个地址空间,一个线程真正拥有的唯一私有存储是处理器寄存器,甚至栈地址也能被共享,底层实现也没有阻止这种访问。但处理线程私有数据的函数可以提高线程间数据的独立性,维护基于每个线程的数据。
在需要一个变量时,如果所有线程共享相同的值,则可以使用静态或外部数据,就像在单线程程序中那样,但通常需要互斥量来同步跨越多个线程对共享数据的存取;如果每个线程都需要一个私有变量值,则必须在某处存储所有值,并且每个线程能够定位到属于自己的值,线程私有数据机制可以做到这一点,线程私有数据避免了与其他线程同步访问的问题。
测试程序:该程序的功能是输出变量名对应的值
在需要一个变量时,如果所有线程共享相同的值,则可以使用静态或外部数据,就像在单线程程序中那样,但通常需要互斥量来同步跨越多个线程对共享数据的存取;如果每个线程都需要一个私有变量值,则必须在某处存储所有值,并且每个线程能够定位到属于自己的值,线程私有数据机制可以做到这一点,线程私有数据避免了与其他线程同步访问的问题。
在分配线程私有数据之前,需要创建与该数据关联的键,然后每个线程就能独立地设定或取得自己的键值。键对所有的线程是相同的,但每个线程能将它独立的键值与共享的键关联。每个线程能在任何时间为键设置它的私有值,而不会影响到其他线程的键值。线程通常使用malloc 为线程私有数据分配内存空间,析构函数通常释放以分配的内存,如果线程没有释放内存就退出了,则会造成内存泄露。
/* 线程私有数据 */
/*
* 函数功能:为线程私有数据创建键值;
* 返回值:若成功则返回0,否则返回错误编码;
* 函数原型:
*/
#include <pthread.h>
int pthread_key_create(pthread_key_t *keyp, void(*destructor)(void*));
/*
* 说明:
* 创建的键值存储在keyp所指向的内存单元中,这个键可以被进程中的所以线程使用,但每个线程把这个键
* 与不同的线程私有数据地址进行关联;创建新键时,每个线程的数据地址设为null;
* 该函数还包含一个键关联析构函数,当线程退出时,若数据地址为非null,则调用析构函数,唯一的参数就是数据地址;
* 若destructor为null时,表示没有析构函数;
*/
/*
* 函数功能:取消线程私有数据与键之间的关联;
* 返回值:若成功则返回0,否则返回错误编码;
* 函数原型:
*/
#include <pthread.h>
int pthread_key_delete(pthread_key_t *key);
对于每个
pthread_key_t 变量只能有一个
pthread_key_create 调用与之对应,如果一个键创建了两次,第二次创建的键将覆盖第一次,第一次的键和任何线程为其设置的值都将丢失。
#include<pthread.h>
pthread_once_t initflag = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *initflag, void(*initfn)(void));
//成功则返回0,否则返回错误编号。
initfalg 必须是一个全局变量或静态变量,而且必须初始化为PTHREAD_ONCE_INIT。它被称之为控制变量,pthread_once 的第二个参数就是与控制变量关联的函数指针,它所指的函数没有参数。
pthread_once 首先检查控制变量,以判断是否已经完成初始化。如果完成,pthread_once 简单地返回;否则,pthread_once 调用初始化函数。如果一个线程在初始化过程中,另外的线程也调用了pthread_once,这后者将等待,直到前面的线程初始化完成。
键一旦创建,就可以用过调用pthread_setspecific函数把键和线程私有数据关联起来,可以通过pthread_getspecific函数获取线程私有数据的地址。
include <pthread.h>
void *pthread_getspecific(pthread_key_t key);
//返回值:线程私有数据值,若没有值与键关联则返回NULL
int pthread_setspecific(pthread_key_t key, const void *value);
//返回值:若成功则返回0,否则返回错误编号
测试程序:该程序的功能是输出变量名对应的值
#include "apue.h"
#include <pthread.h>
extern char **environ;
pthread_mutex_t env_mutex;
static pthread_key_t key;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;
static void thread_init(void);
char *Mgetenv(const char *name);
void *fun1(void *arg);
void *fun2(void *arg);
int main()
{
pthread_t tid1,tid2;
int err;
void *pret;
err = pthread_create(&tid1,NULL,fun1,NULL);
if(err != 0)
err_quit("can't create thread: %s\n", strerror(err));
err = pthread_create(&tid2,NULL, fun2,NULL);
if(err != 0)
err_quit("can't create thread: %s\n", strerror(err));
pthread_join(tid1,&pret);
printf("thread 1 exit code is: %d\n",(int)pret);
pthread_join(tid2,&pret);
printf("thread 2 exit code is: %d\n",(int)pret);
exit(0);
}
static void thread_init(void)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&env_mutex,&attr);
pthread_mutexattr_destroy(&attr);
pthread_key_create(&key,free);
}
char *Mgetenv(const char *name)
{
int i,len;
char *envbuf;
pthread_once(&init_done,thread_init);
pthread_mutex_lock(&env_mutex);
envbuf = (char*)pthread_getspecific(key);
if(envbuf == NULL)
{
envbuf = (char*)malloc(ARG_MAX);
if(envbuf == NULL)
{
pthread_mutex_unlock(&env_mutex);
return NULL;
}
pthread_setspecific(key,envbuf);
}
len = strlen(name);
for(i=0; environ[i] != NULL; i++)
{
if((strncmp(name, environ[i], len) == 0) &&
(environ[i][len] == '='))
{
strcpy(envbuf, &environ[i][len+1]);
pthread_mutex_unlock(&env_mutex);
return envbuf;
}
}
pthread_mutex_unlock(&env_mutex);
return NULL;
}
void *fun1(void *arg)
{
char *value;
printf("thread 1 start...\n");
value = Mgetenv("HOME");
printf("HOME=%s\n",value);
printf("thread 1 exit...\n");
pthread_exit((void*)1);
}
void *fun2(void *arg)
{
char *value;
printf("thread 2 start...\n");
value = Mgetenv("SHELL");
printf("SHELL=%s\n",value);
printf("thread 2 exit...\n");
pthread_exit((void*)2);
}
参考资料:
《UNIX高级环境编程》
http://www.cnblogs.com/Anker/archive/2012/12/19/2824990.html