[转] unix/linux下线程私有数据实现原理及使用方法
在维护每个线程的私有数据的时候,我们可能会想到分配一个保存线程数据的数组,用线程的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下线程私有数据实现原理及使用方法的更多相关文章
- linux线程私有数据---TSD池
进程内的所有线程共享进程的数据空间,所以全局变量为所有线程共有.在某些场景下,线程需要保存自己的私有数据,这时可以创建线程私有数据(Thread-specific Data)TSD来解决.在线程内部, ...
- linux多线程学习笔记六--一次性初始化和线程私有数据【转】
转自:http://blog.csdn.net/kkxgx/article/details/7513278 版权声明:本文为博主原创文章,未经博主允许不得转载. 一,一次性初始化 以保证线程在调用资源 ...
- Linux系统编程——线程私有数据
在多线程程序中.常常要用全局变量来实现多个函数间的数据共享.因为数据空间是共享的,因此全局变量也为全部线程共同拥有. 測试代码例如以下: #include <stdio.h> #inclu ...
- ZT linux 线程私有数据之 一键多值技术
这个原作者的这个地方写错了 且他举的例子非常不好.最后有我的修正版本 pthread_setspecific(key, (void *)&my_errno); linux 线程私有数据之一键多 ...
- UNIX环境高级编程——线程私有数据
线程私有数据(Thread-specific data,TSD):存储和查询与某个线程相关数据的一种机制. 在进程内的所有线程都共享相同的地址空间,即意味着任何声明为静态或外部变量,或在进程堆声明的变 ...
- linux下线程
linux下线程 线程与进程的关系: 之前转载的微信文章,进程与线程的差别已经说得比較清楚了.能够查看之前转载的文章.linux进程与线程的差别. 创建一个线程: #include<pthrea ...
- Posix线程编程指南(2) 线程私有数据
概念及作用 在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据.在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有.但有时应用程序设计中有必要提供 ...
- Linux 下线程的理解
2017-04-03 最近深入研究了下Linux线程的问题,发现自己之前一直有些许误解,特记之…… 关于Linux下的线程,各种介绍Linux的书籍都没有深入去解释的,或许真的如书上所述,Linux本 ...
- Thread-specific data(TSD)线程私有数据
Thread-specific data(TSD)线程私有数据 http://blog.chinaunix.net/uid-26885237-id-3209913.html linux多线程编程中引入 ...
随机推荐
- rpm-bin
bin:二进制可执行程序,与windows的exe文件一样,在linux图形界面可直接双击运行,或在终端界面使用该命令执行 ./filename 有的软件是二进制安装程序和源代码一起发布,二进制程序文 ...
- 使用SQL语句创建和删除约束
原文:http://blog.csdn.net/hamber_bao/article/details/6504905 约束的目的就是确保表中的数据的完整性. 常用的约束类型如下: 主键约束:(Prim ...
- hdu 2031
水题 AC代码: #include<iostream> using namespace std; int main() { int i,j,b[1000],k; while(cin> ...
- Highcharts使用=====通过指定日期显示曲线
1.说明: 利用HighStock显示曲线,在右上角的日期间隔选择好日期后,重新请求后台数据,重新加载曲线. 2.实现方法: 在HighStock的rangeSelector中有一个属性inputDa ...
- C#Socket 案例
服务器端 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; ...
- 自己手动绿色化MyEclipse
绿化过程因每个人的文件存放路径不同而不同 首先打开你解压的MyEclipse文件,或者以前安装的MyEclipse重装系统后不能用,打开到这里:记住路径,比如我的是:D:\MyEclipse 我们打开 ...
- AJAX 跨域
1.说到ajax就会遇到的两个问题 1.1AJAX以何种格式来交换数据 1.自定义字符串 2.XML描述 3.JSON描述(建议使用) 1.2如 ...
- ecstore生成二维码
利用phpqrcode库生成二维码: /* *二维码添加 */ ////////////////////////////////////// /*引入文件*/ @include(APP_DIR.'/i ...
- 转载自php 大牛的学习计划 人生规划
2012年偶决定开始写博客了,不为别的,就希望可以通过博客记录我的成长历程同时也希望可以帮助一些刚毕业,刚入行业的兄弟姐们们.我们是一群充满浮躁.抱怨.迷茫的程序猿,想一想3年就这么过去了,社会变得更 ...
- python 3.5 格式化字符串输出
#!/usr/bin/env python #encoding: utf-8 #.strip('里面可以去掉字符串中两边的字符') name = input('name :').strip(' ') ...