unix/linux下线程私有数据实现原理及使用方法

26 篇文章 0 订阅

转载自:http://blog.csdn.net/caigen1988/article/details/7901248

复习以前做过的爬虫系统,在做防止死链时用到了线程的私有数据,那时候仅仅是会用,至于底层是怎么实现的以及为什么一个键可以对应不同线程的指还一知半解,今天又详细的看了下相关的资料,以做总结。

      在维护每个线程的私有数据的时候,我们可能会想到分配一个保存线程数据的数组,用线程的ID作为数组的索引来实现访问,但是有一个问题是系统生成的线程ID不能保证是一个小而连续的整数,并且用数组实现的时候由于其他线程也可以访问其数组中的数据,这样会引起数据混乱。这时候我们可以借助线程的私有数据来解决这个问题。

      线程私有数据实现的主要思想是:在分配线程私有数据之前,创建与该数据相关联的健,这个键可以被进程中的所有线程使用,但每个线程把这个键与不同的线程私有数据地址进行关联,需要说明的是每个系统支持有限数量的线程特定数据元素(下面的例子以128个为限制)。那么这个键的实现原理是什么呢?

其实系统为每个进程维护了一个称之为Key结构的结构数组,如下图所示:

(图1)

在上图中Key 结构的“标志”指示这个数据元素是否正在使用。在刚开始时所有的标志初始化为“不在使用”。当一个线程调用pthread_key_create创建一个新的线程特定数据元素时,系统会搜索Key结构数组,找出第一个“不在使用”的元素。并把该元素的索引(0~127)称为“键”。 返回给调用线程的正是这个索引。

除了进程范围内的Key结构数组之外,系统还在进程内维护了关于多个线程的多条信息。这些特定于线程的信息我们称之为Pthread结构。其中部分内容是我们称之为pkey数组的一个128个元素的指针数组。系统维护的关于每个线程的信息结构图如下:

(图2)

 

在上图中,pkey数组所有元素都被初始化为空指针。这些128个指针是和进程内128个可能的键逐一关联的值。

那么当我们调用pthread_key_create函数时,系统会为我们做什么呢?

    系统首先会返回给我们一个Key结构数组中第一个“未被使用”的键(即索引值),每个线程可以随后通过该键找到对应的位置,并且为这个位置存储一个值(指针)。 一般来说,这个指针通常是每个线程通过调用malloc来获得的。

知道了大概的私有数据实现的原理,那么在编程中如何使用线程的特定数据呢?

假设一个进程被启动,并且多个线程被创建。 其中一个线程调用pthread_key_create。系统在Key结构数组(图1)中找到第1个未使用的元素。并把它的索引(0~127)返回给调用者。我们假设找到的索引为1  (我们会使用pthread_once 函数确保pthread_key_create只被调用一次,这个在以后会讲到)。

之后线程调用pthread_getspecific获取本线程的pkey[1] 的值(图(2)中键1所值的指针), 返回值是一个空值,线程那么调用malloc分配内存区并初始化此内存区。 之后线程调用pthread_setspecific把对应的所创建键的线程特定数据指针(pkey[1]) 设置为指向它刚刚分配的内存区。下图指出了此时的情形。

(图三)

 

明白了怎样获取线程的特定数据值,那么如果线程终止时系统会执行什么操作呢?

我们知道,一个线程调用pthread_key_create创建某个特定的数据元素时,所指定的参数之一便是指向牧歌析构函数的指针。当一个线程终止时,系统将扫描该线程的pkey数组,为每个非空的pkey指针调用相应的析构函数。 相应的析构函数是存放在图1中的Key数组中的函数指针。这是一个线程终止时其线程特定数据的释放手段。

明白了线程私有数据的实现原理,我们就来看一下相应函数的用法:

#include<pthread.h>

int phread_once(pthread_once_t *onceptr, vid(*init)(void));

in pthread_key_create(pthread_key_t *keyptr, void(* destructor)(void *value));

注意:pthread_once 使用onceptr 参数指向的变量中的值确保init参数所指的函数在进程范围内之被调用一次,onceptr必须是一个非本地变量(即全局变量或者静态变量),而且必须初始化为PTHREAD_ONCE_INIT。

这两个函数的典型用法如下:

pthread_key_t r1_key;

pthread_once_t r1_once = PTHREAD_ONCE_INIT;

void destructor(void *ptr)

{

     free(ptr);

}

void excute_once(void)  // 确保键只被创建一次

{

    pthread_key_create(&r1_key, destructor);

}

int main()

{

   pthread_once(&r1_once, excute_once);

}

 

下面还有两个设置线程私有数据和获得线程私有数据的函数

void *pthread_getspecific(pthread_key_t key);

int pthread_setspecific(pthread_key_t key, const void *value); -- 0 返回成功  返回其它值表示出错。

 

以下是APUE利用线程私有数据实现线程安全的getenv代码

[cpp]  view plain copy
  1. #include <limits.h>  
  2. #include <string.h>  
  3. #include <pthread.h>  
  4. #include <stdlib.h>  
  5. static pthread_key_t key;  
  6. static pthread_once_t init_done = PTHREAD_ONCE_INIT;  
  7. pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;  
  8. extern char **environ;  
  9. static void  
  10. thread_init(void)  
  11. {  
  12. pthread_key_create(&key, free);  
  13. }  
  14. char *  
  15. getenv(const char *name)  
  16. {  
  17. int i, len;  
  18. char *envbuf;  
  19. pthread_once(&init_done, thread_init);  
  20. pthread_mutex_lock(&env_mutex);  
  21. envbuf = (char *)pthread_getspecific(key);  
  22. if (envbuf == NULL) {  
  23. envbuf = malloc(ARG_MAX);  
  24. if (envbuf == NULL) {  
  25. pthread_mutex_unlock(&env_mutex);  
  26. return(NULL);  
  27. }  
  28. pthread_setspecific(key, envbuf);  
  29. }  
  30. len = strlen(name);  
  31. for (i = 0; environ[i] != NULL; i++) {  
  32. if ((strncmp(name, environ[i], len) == 0) &&  
  33. (environ[i][len] == '=')) {  
  34. strcpy(envbuf, &environ[i][len+1]);  
  35. pthread_mutex_unlock(&env_mutex);  
  36. return(envbuf);  
  37. }  
  38. }  
  39. pthread_mutex_unlock(&env_mutex);  
  40. return(NULL);  
  41. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值