unix 环境高级编程 线程私有数据

本文详细介绍了线程私有数据的概念及其应用场景,包括为何需要线程私有数据、如何创建和使用线程私有数据键,以及如何通过示例代码来管理线程私有数据。

线程私有数据:

        对于这个名词,我最开始在看apue上面的解释的时候是这样子的:对于一个全局变量,比如说是x,我们通过在每个线程中创建变量x的副本,从而使得在每个线程中使用副本x而不会影响到全局变量x的值,但是后来我又想了想,这样子不对啊,因为如果真的是这样子的话,那么完全没有必要创立什么键值啊,直接在各个线程中创建局部变量,然后再通过全局变量进行赋值就好了。这是我最开始的理解,感觉真的很荒谬,难道是我理解错了,所以就开始查博客还有就是看看专门的POSIX多线程的书,终于让我找到了答案,确实是我理解错了,下面是我后来的一些看法,有网上的,也有书上的。

      这是网上的第一篇博文:感觉是和《POSIX多线程程序设计》上没啥区别。。。

       

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......

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值