在维护每个线程的私有数据的时候,我们可能会想到分配一个保存线程数据的数组,用线程的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_c

reate(&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 返回成功  返回其它值表示出错。

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

 1 #include <limits.h>
2 #include <string.h>
3 #include <pthread.h>
4 #include <stdlib.h>
5
6 static pthread_key_t key;
7 static pthread_once_t init_done = PTHREAD_ONCE_INIT;
8 pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;
9 extern char **environ;
10
11 static void thread_init(void)
12 {
13 pthread_key_create(&key, free);
14 }
15 char * 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 nvbuf = malloc(ARG_MAX);
24 if (envbuf == NULL) {
25 pthread_mutex_unlock(&env_mutex);
26 return(NULL);
27      }
28   pthread_setspecific(key, envbuf);
29   }
30
31   len = strlen(name);
32   for (i = 0; environ[i] != NULL; i++) {
33     if ((strncmp(name, environ[i], len) == 0) && (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 }

[转] unix/linux下线程私有数据实现原理及使用方法的更多相关文章

  1. linux线程私有数据---TSD池

    进程内的所有线程共享进程的数据空间,所以全局变量为所有线程共有.在某些场景下,线程需要保存自己的私有数据,这时可以创建线程私有数据(Thread-specific Data)TSD来解决.在线程内部, ...

  2. linux多线程学习笔记六--一次性初始化和线程私有数据【转】

    转自:http://blog.csdn.net/kkxgx/article/details/7513278 版权声明:本文为博主原创文章,未经博主允许不得转载. 一,一次性初始化 以保证线程在调用资源 ...

  3. Linux系统编程——线程私有数据

    在多线程程序中.常常要用全局变量来实现多个函数间的数据共享.因为数据空间是共享的,因此全局变量也为全部线程共同拥有. 測试代码例如以下: #include <stdio.h> #inclu ...

  4. ZT linux 线程私有数据之 一键多值技术

    这个原作者的这个地方写错了 且他举的例子非常不好.最后有我的修正版本 pthread_setspecific(key, (void *)&my_errno); linux 线程私有数据之一键多 ...

  5. UNIX环境高级编程——线程私有数据

    线程私有数据(Thread-specific data,TSD):存储和查询与某个线程相关数据的一种机制. 在进程内的所有线程都共享相同的地址空间,即意味着任何声明为静态或外部变量,或在进程堆声明的变 ...

  6. linux下线程

    linux下线程 线程与进程的关系: 之前转载的微信文章,进程与线程的差别已经说得比較清楚了.能够查看之前转载的文章.linux进程与线程的差别. 创建一个线程: #include<pthrea ...

  7. Posix线程编程指南(2) 线程私有数据

    概念及作用 在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据.在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有.但有时应用程序设计中有必要提供 ...

  8. Linux 下线程的理解

    2017-04-03 最近深入研究了下Linux线程的问题,发现自己之前一直有些许误解,特记之…… 关于Linux下的线程,各种介绍Linux的书籍都没有深入去解释的,或许真的如书上所述,Linux本 ...

  9. Thread-specific data(TSD)线程私有数据

    Thread-specific data(TSD)线程私有数据 http://blog.chinaunix.net/uid-26885237-id-3209913.html linux多线程编程中引入 ...

随机推荐

  1. .NET基础拾遗(7)多线程开发基础4

    一.多线程编程中的线程同步 1.C#中的lock关键字 lock关键字可能是我们在遇到线程同步的需求时最常用的方式,但lock只是一个语法糖,为什么这么说呢,下面慢慢道来. (1)lock的等效代码其 ...

  2. C# typeof Gettype is as &拆箱 装箱

    有时候,我们不想用值类型的值,就是想用一个引用..Net提供了一个名为装箱(boxing)的机制,它允许根据值类型来创建一个对象,然后使用对这个新对象的一个引用. 首先,回顾两个重要的事实,1.对于引 ...

  3. django: urlconfig

    django 的 url 配置主要在 urls.py 中进行 urlconfig 中对 url 的处理方式主要在: 一 视图处理方式 如 上文 例子所示: url(r'^blog/index/$', ...

  4. htm初学笔记

    一.什么是html HTML(HyperText Markup Language):超文本标记语言,一种纯文本类型的语言 --使用带有尖括号的“标记”将网页中的内容逐一标识出来 用来设计网页的标记语言 ...

  5. HTML与CSS入门——第六章 使用字体

    知识点: 1.粗体.斜体和特殊文本格式的使用 2.字体的调整方法 3.特殊字符的使用方法 6.1 粗体.斜体和特殊文本格式: font-weight控制粗细 加粗<strong> font ...

  6. (转)弹出窗口lhgDialog API文档

    应用到你的项目 如果您使用独立版本的lhgDialog窗口组件,您只需在页面head中引入lhgcore.lhgdialog.min.js文件,4.1.1+版本做了修改可以和jQuerya库同时引用, ...

  7. C#--对象的相等比较

    对象相等比较机制对于引用类型的变量和值类型的变量来说是不同的,下面分别介绍引用类型和值类型的相等比较. 首先来看System.Object的部分定义: public class Object { // ...

  8. 你好,C++(19)“老师,我这次四级考试过了没有?”——4.2 条件选择语句

    4.2  条件选择语句 “老师,我这次四级考试过了没有?” 如果老师被问到这个问题,他会如何回答?是的,他会根据不同的条件选择不同的回答: 如果考试成绩大于等于60,那就回答:“恭喜你,你通过了这次考 ...

  9. 使用自定义类型做qmap,qhash的key

    map在STL中的定义 template <class Key, class T, class Compare = less<Key>, class Alloc = alloc> ...

  10. CSS应用五

    1. 页面变灰 html {   filter: grayscale(100%);//IE浏览器   -webkit-filter: grayscale(100%);//谷歌浏览器   -moz-fi ...