线程私有数据
2015-08-19 16:57
603 查看
原文地址:http://blog.chinaunix.net/uid-8917757-id-2450452.html
1.为什么需要线程私有数据:
原因一:有时候需要维护基于每个线程的数据,用线程ID作为索引。因为线程ID不能保证是小而连续的整数,所以不能简单的分配一个线程数据数组,用线程ID作为数组的索引。即使线程ID确实是小而连续的整数,可能还希望有一些额外的保护,以防止某个线程的数据和其它线程的数据相混淆。
原因二:可以让基于进程的接口适应多线程环境,比如errno,线程出现以前errno被定义成进程环境中全局可访问的整数,线程出现以后,为了让线程也能使用那些原本基于进程的系统调用和库例程,errno被重新定义成线程私有数据。
(参考APUE2)
2.进程中的所有线程都可以访问进程的整个地址空间,除非使用寄存器(一个线程真正拥有的唯一私有存储是处理器寄存器),线程没有办法阻止其它线程访问它的数据,线程私有数据也不例外,但是管理线程私有数据的函数可以提高线程间的数据独立性。
3.int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));
在分配(malloc)线程私有数据之前,需要创建和线程私有数据相关联的键(key),这个键的功能是获得对线程私有数据的访问权。
如果创建一个线程私有数据键,必须保证Pthread_key_create对于每个Pthread_key_t变量仅仅被调用一次,因为如果一个键被创建两次,其实是在创建两个不同的键,第二个键将覆盖第一个键,第一个键以及任何线程可能为其关联的线程私有数据值将丢失。
创建新键时,每个线程的数据地址设为NULL。
4.关于析构函数void (*destructor)(void *):
当线程退出时,如果线程私有数据地址被置为非NULL值,那么析构函数就会被调用。
当线程调用pthread_exit或者线程执行返回,正常退出时,析构函数就会被调用,但是如果线程调用了exit系列函数或者abort或者其它非正常退出时,就不会调用析构函数。
5.线程退出时,线程私有数据的析构函数将按照OS实现定义的顺序被调用。析构函数可能调用另外一个函数,而该函数可能创建新的线程私有数据而且把这个线程私有数据和当前的键关联起来。当所有的析构函数都调用完成以后,系统会检查是否有非NULL的线程私有数据值与键关联,如果有的话,再次调用析构函数,这个过程一直重复到线程所有的键都为NULL值线程私有数据,或者已经做了PTHREAD_DESTRUCTOR_ITERATIONS中定义的最大次数的尝试.
当下爱你成退出时,Pthreads在进程汇总检查所有的线程私有数据键,并且将不是NULL的线程私有数据简置NULL,然后调用键的destructor函数。
6.int pthread_delete(pthread_key_t *keyp),注意调用pthread_delete不会激活与键关联的析构函数,容易造成内存泄露。
当删除线程私有数据键的时候,不会影响任何线程对该键设置的线程私有数据值,甚至不影响调用线程当前键值,所以容易造成内存泄露,如果你不记得释放所有线程内与该键相关联的私有数据空间的话。
使用已经删除的私有数据键将导致未定义的行为。
编程建议:最后不删除线程私有数据键!!!尤其当一些线程仍然持有该键的值时,就更不该释放该键!!!
7.需要确保分配的键不会由于初始化阶段的竞争而发生变动。(使用pthread_once避免)
容易发生竞争的代码段:
void destructor(void *);
pthread_key_t key;
int flag;
int threadfun(void *arg)
{
if(!flag){
flag = 1;
pthread_key_create(&key, destructor);
}
}
使用pthread_once:
void destructor(void *);
pthread_once_t initonce = PTHREAD_ONCE_INIT;
pthread_key_t key;
void thread_once(void)
{
pthread_key_create(&key, destructor);
}
int threadfun(void *arg)
{
pthread_once(&initonce, thread_once);
...
}
8.void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
if(pthread_getspecific(key) == NULL){
printf("需要调用pthread_setspecific");
}
关于线程私有数据的简单代码:
执行结果:
[xxxx@localhost chap5]$ ./a.out
Initializing key
thread 2 set tsd value at 0x8eab8e8
thread 2 starting......
thread 1 set tsd value at 0x8eab8d8
thread 1 starting......
thread 2 done......
thread 1 done......
关于析构函数使用的简单实例:
1.为什么需要线程私有数据:
原因一:有时候需要维护基于每个线程的数据,用线程ID作为索引。因为线程ID不能保证是小而连续的整数,所以不能简单的分配一个线程数据数组,用线程ID作为数组的索引。即使线程ID确实是小而连续的整数,可能还希望有一些额外的保护,以防止某个线程的数据和其它线程的数据相混淆。
原因二:可以让基于进程的接口适应多线程环境,比如errno,线程出现以前errno被定义成进程环境中全局可访问的整数,线程出现以后,为了让线程也能使用那些原本基于进程的系统调用和库例程,errno被重新定义成线程私有数据。
(参考APUE2)
2.进程中的所有线程都可以访问进程的整个地址空间,除非使用寄存器(一个线程真正拥有的唯一私有存储是处理器寄存器),线程没有办法阻止其它线程访问它的数据,线程私有数据也不例外,但是管理线程私有数据的函数可以提高线程间的数据独立性。
3.int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));
在分配(malloc)线程私有数据之前,需要创建和线程私有数据相关联的键(key),这个键的功能是获得对线程私有数据的访问权。
如果创建一个线程私有数据键,必须保证Pthread_key_create对于每个Pthread_key_t变量仅仅被调用一次,因为如果一个键被创建两次,其实是在创建两个不同的键,第二个键将覆盖第一个键,第一个键以及任何线程可能为其关联的线程私有数据值将丢失。
创建新键时,每个线程的数据地址设为NULL。
4.关于析构函数void (*destructor)(void *):
当线程退出时,如果线程私有数据地址被置为非NULL值,那么析构函数就会被调用。
当线程调用pthread_exit或者线程执行返回,正常退出时,析构函数就会被调用,但是如果线程调用了exit系列函数或者abort或者其它非正常退出时,就不会调用析构函数。
5.线程退出时,线程私有数据的析构函数将按照OS实现定义的顺序被调用。析构函数可能调用另外一个函数,而该函数可能创建新的线程私有数据而且把这个线程私有数据和当前的键关联起来。当所有的析构函数都调用完成以后,系统会检查是否有非NULL的线程私有数据值与键关联,如果有的话,再次调用析构函数,这个过程一直重复到线程所有的键都为NULL值线程私有数据,或者已经做了PTHREAD_DESTRUCTOR_ITERATIONS中定义的最大次数的尝试.
当下爱你成退出时,Pthreads在进程汇总检查所有的线程私有数据键,并且将不是NULL的线程私有数据简置NULL,然后调用键的destructor函数。
6.int pthread_delete(pthread_key_t *keyp),注意调用pthread_delete不会激活与键关联的析构函数,容易造成内存泄露。
当删除线程私有数据键的时候,不会影响任何线程对该键设置的线程私有数据值,甚至不影响调用线程当前键值,所以容易造成内存泄露,如果你不记得释放所有线程内与该键相关联的私有数据空间的话。
使用已经删除的私有数据键将导致未定义的行为。
编程建议:最后不删除线程私有数据键!!!尤其当一些线程仍然持有该键的值时,就更不该释放该键!!!
7.需要确保分配的键不会由于初始化阶段的竞争而发生变动。(使用pthread_once避免)
容易发生竞争的代码段:
void destructor(void *);
pthread_key_t key;
int flag;
int threadfun(void *arg)
{
if(!flag){
flag = 1;
pthread_key_create(&key, destructor);
}
}
使用pthread_once:
void destructor(void *);
pthread_once_t initonce = PTHREAD_ONCE_INIT;
pthread_key_t key;
void thread_once(void)
{
pthread_key_create(&key, destructor);
}
int threadfun(void *arg)
{
pthread_once(&initonce, thread_once);
...
}
8.void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
if(pthread_getspecific(key) == NULL){
printf("需要调用pthread_setspecific");
}
关于线程私有数据的简单代码:
#include "../errors.h" typedef struct tsd_tag{ pthread_t thread_id; char *string; }tsd_t; pthread_key_t key; pthread_once_t once = PTHREAD_ONCE_INIT; void once_routine(void) { int status; printf("Initializing key\n"); status = pthread_key_create(&key, NULL); if(status != 0){ err_abort(status, "pthread_key_create"); } } void *thread_routine(void *arg) { int status; tsd_t *value = NULL; status = pthread_once(&once, once_routine); if(status != 0){ err_abort(status, "pthread_once"); } value = (tsd_t *)malloc(sizeof(tsd_t)); if(value == NULL){ errno_abort("malloc"); } status = pthread_setspecific(key, (void *)value); if(status != 0){ err_abort(status, "pthread_setspecific"); } printf("%s set tsd value at %p\n", (char *)arg, value); value->thread_id = pthread_self(); value->string = (char *)arg; printf("%s starting......\n", (char *)arg); sleep(2); value = (tsd_t *)pthread_getspecific(key); if(value == NULL){ printf("no thread-specific data value was associated \ with key\n"); pthread_exit(NULL); } printf("%s done......\n", value->string); } int main(int argc, char **argv) { pthread_t thread1, thread2; int status; status = pthread_create(&thread1, NULL, thread_routine, "thread 1"); if(status != 0){ err_abort(status, "create thread1"); } status = pthread_create(&thread2, NULL, thread_routine, "thread 2"); if(status != 0){ err_abort(status, "create thread2"); } pthread_exit(NULL); } |
[xxxx@localhost chap5]$ ./a.out
Initializing key
thread 2 set tsd value at 0x8eab8e8
thread 2 starting......
thread 1 set tsd value at 0x8eab8d8
thread 1 starting......
thread 2 done......
thread 1 done......
关于析构函数使用的简单实例:
#include "../errors.h" typedef struct private_tag{ pthread_t thread_id; char *string; }private_t; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_key_t key; int count = 0; void *key_get(void) { void *value = NULL; int status; value = pthread_getspecific(key); if(value == NULL){ value = malloc(sizeof(pthread_key_t)); if(value == NULL){ errno_abort("malloc"); } status = pthread_setspecific(key, value); if(status != 0){ err_abort(status, "pthread_setspecific"); } } return value; } void *thread_routine(void *arg) { private_t *value = NULL; value = (private_t *)key_get(); value->thread_id = pthread_self(); value->string = (char *)arg; printf("\"%s\" starting......\n", value->string); sleep(2); } void destructor(void *arg) { private_t *private = (private_t *)arg; int status; printf("\"%s\" exiting......\n", private->string); free(arg); status = pthread_mutex_lock(&mutex); if(status != 0){ err_abort(status, "mutex lock"); } count--; if(count <= 0){ status = pthread_key_delete(key); if(status != 0){ err_abort(status, "pthread_delete"); } printf("key deleted......\n"); } status = pthread_mutex_unlock(&mutex); if(status != 0){ err_abort(status, "mutex unlock"); } } int main(int argc, char **argv) { int status; pthread_t thread1, thread2; private_t *value = NULL; status = pthread_key_create(&key, destructor); if(status != 0){ err_abort(status, "pthread_key_create"); } count = 3; value = (private_t *)key_get(); value->thread_id = pthread_self(); value->string = "main thread"; status = pthread_create(&thread1, NULL, thread_routine,"thread 1"); if(status != 0){ err_abort(status, "create thread1"); } status = pthread_create(&thread2, NULL, thread_routine,"thread 2"); if(status != 0){ err_abort(status, "create thread2"); } pthread_exit(NULL); } 执行结果: [xxxx@localhost chap5]$ ./a.out "main thread" exiting...... "thread 1" starting...... "thread 2" starting...... "thread 1" exiting...... "thread 2" exiting...... key deleted...... |
相关文章推荐
- 续写《一个声音喊到底》与高三21班…
- 一个声音喊到底
- 桌面支持--outlook取消收件规则1
- 上海某高校的论坛统计信息。
- APK 的安装与卸载
- 开博第一篇
- Http Digest 认证在android平台的实现
- 1081. Rational Sum
- js只需5分钟创建一个跨三大平台纯原生APP
- WCF通过IIS寄宿服务
- 桌面支持--outlook会议邀请(选择可选参加的领导)
- 第三方登入和分享
- 能力风暴机器人AS-MF2011循迹算法
- php02
- App 从Windows到OS X的移植
- 使用确定对话框来控制循环
- select @@identity
- Duilib界面库Capture系列的相关bug解决
- HTML
- nmap使用参考