Linux多线程实践(4) --线程特定数据
线程特定数据
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)); int pthread_key_delete(pthread_key_t key); int pthread_setspecific(pthread_key_t key, const void *pointer); void * pthread_getspecific(pthread_key_t key); pthread_once_t once_control = PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据, 然而在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问。POSIX线程库通过维护一定的数据结构来解决这个问题,这个些数据称为(Thread-specific-data或 TSD), 线程特定数据如下图所示:
从上图可知:当调用pthread_key_create 后会产生一个所有线程都可见的线程特定数据(TSD)的键值(如上图中所有的线程都会得到一个pkey[1]的值), 但是这个键所指向的真实数据却是不同的,虽然都是pkey[1], 但是他们并不是指向同一块内存,而是指向了只属于自己的实际数据, 因此, 如果线程0更改了pkey[1]所指向的数据, 而并不能够影像到线程n;
在线程调用pthread_setspecific后会将每个线程的特定数据与thread_key_t绑定起来,虽然只有一个pthread_key_t,但每个线程的特定数据是独立的内存空间,当线程退出时会执行destructor 函数。
/** 示例1: 设置/获取线程特定数据
在两个线程中分别设置/获取线程特定数据, 查看两个线程中的数据是否是一样的(肯定是不一样的O(∩_∩)O~)
**/
pthread_key_t key;
typedef struct Tsd
{
pthread_t tid;
char *str;
} tsd_t;
//用来销毁每个线程所指向的实际数据
void destructor_function(void *value)
{
free(value);
cout << "destructor ..." << endl;
}
void *thread_routine(void *args)
{
//设置线程特定数据
tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
value->tid = pthread_self();
value->str = (char *)args;
pthread_setspecific(key, value);
printf("%s setspecific, address: %p\n", (char *)args, value);
//获取线程特定数据
value = (tsd_t *)pthread_getspecific(key);
printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
sleep(2);
//再次获取线程特定数据
value = (tsd_t *)pthread_getspecific(key);
printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
pthread_exit(NULL);
}
int main()
{
//这样每个线程当中都会有一个key可用了,
//但是每个key所绑定的实际区域需要每个线程自己指定
pthread_key_create(&key, destructor_function);
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, thread_routine, (void *)"thread1");
pthread_create(&tid2, NULL, thread_routine, (void *)"thread2");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_key_delete(key);
return 0;
}
/** 示例2:运用pthread_once, 让key只初始化一次
注意: 将对key的初始化放入到init_routine中
**/
pthread_key_t key;
pthread_once_t once_control = PTHREAD_ONCE_INIT;
typedef struct Tsd
{
pthread_t tid;
char *str;
} tsd_t;
//线程特定数据销毁函数,
//用来销毁每个线程所指向的实际数据
void destructor_function(void *value)
{
free(value);
cout << "destructor ..." << endl;
}
//初始化函数, 将对key的初始化放入该函数中,
//可以保证inti_routine函数只运行一次
void init_routine()
{
pthread_key_create(&key, destructor_function);
cout << "init..." << endl;
}
void *thread_routine(void *args)
{
pthread_once(&once_control, init_routine);
//设置线程特定数据
tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
value->tid = pthread_self();
value->str = (char *)args;
pthread_setspecific(key, value);
printf("%s setspecific, address: %p\n", (char *)args, value);
//获取线程特定数据
value = (tsd_t *)pthread_getspecific(key);
printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
sleep(2);
//再次获取线程特定数据
value = (tsd_t *)pthread_getspecific(key);
printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
pthread_exit(NULL);
}
int main()
{
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, thread_routine, (void *)"thread1");
pthread_create(&tid2, NULL, thread_routine, (void *)"thread2");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_key_delete(key);
return 0;
}
附-Linux/Unix线程私有数据实现思想:
原文连接:http://blog.csdn.net/caigen1988/article/details/7901248
线程私有数据实现的主要思想是:在分配线程私有数据之前,创建与该数据相关联的键,这个键可以被进程中的所有线程使用,但每个线程把这个键与不同的线程私有数据地址进行关联,需要说明的是每个系统支持有限数量的线程特定数据元素(如:限制为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_getspecific获取本线程的pkey[1] 的值(图(2)中键1所值的指针), 返回值是一个空值,线程那么调用malloc分配内存区并初始化此内存区。 之后线程调用pthread_setspecific把对应的所创建键的线程特定数据指针(pkey[1]) 设置为指向它刚刚分配的内存区。下图指出了此时的情形。
(图3)
明白了怎样获取线程的特定数据值,那么如果线程终止时系统会执行什么操作呢?
我们知道,一个线程调用pthread_key_create创建某个特定的数据元素时,所指定的参数之一便是指向析构函数的指针。当一个线程终止时,系统将扫描该线程的pkey数组,为每个非空的pkey指针调用相应的析构函数。 相应的析构函数是存放在图1中的Key数组中的函数指针。这是一个线程终止时其线程特定数据的释放手段。
Linux多线程实践(4) --线程特定数据的更多相关文章
- Linux多线程实践(1) --线程理论
线程概念 在一个程序里的一个执行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列/指令序列"; 一切进程至少有一个执行线程; 进程 VS. 线程 ...
- Linux多线程实践(2) --线程基本API
POSIX线程库 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以"pthread_"开头,要使用这些函数库,要通过引入头文<pthread.h>,而且链 ...
- Linux多线程实践(3) --线程属性
初始化/销毁线程属性 int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *att ...
- Linux多线程实践(四 )线程的特定数据
在单线程程序中.我们常常要用到"全局变量"以实现多个函数间共享数据, 然而在多线程环境下.因为数据空间是共享的.因此全局变量也为全部线程所共同拥有.但有时应用程序设计中有必要提供线 ...
- Linux多线程实践(10) --使用 C++11 编写 Linux 多线程程序
在这个多核时代,如何充分利用每个 CPU 内核是一个绕不开的话题,从需要为成千上万的用户同时提供服务的服务端应用程序,到需要同时打开十几个页面,每个页面都有几十上百个链接的 web 浏览器应用程序,从 ...
- 线程的属性和 线程特定数据 Thread-specific Data
一.posix 线程属性 POSIX 线程库定义了线程属性对象 pthread_attr_t ,它封装了线程的创建者可以访问和修改的线程属性.主要包括如下属性: 1. 作用域(scope) 2. 栈尺 ...
- 线程特定数据TSD总结
一线程的本质 二线程模型的引入 三线程特定数据 四关键函数说明 五刨根问底啥原理 六私有数据使用演示样例 七參考文档 一.线程的本质 Linux线程又称轻量进程(LWP),也就说线程本质是用进程之间共 ...
- pthread线程特定数据
举个栗子 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/t ...
- muduo网络库源码学习————线程特定数据
muduo库线程特定数据源码文件为ThreadLocal.h //线程本地存储 // Use of this source code is governed by a BSD-style licens ...
随机推荐
- Linux的管理类命令及其使用方法
文件操作相关有一些命令可以帮助我们"修剪"之前看到的文件树. $touch a.txt 如果a.txt不存在,生成一个新的空文档a.txt.如果a.txt存在,那么只更改该文档的时 ...
- spark升级后 集成hbase-1.0.0-cdh5.4.5异常
.具体场景如下: spark1.6 升级 spark2.2 后 分析查询hbase 数据报异常: 具体错误如下: ERROR TableInputFormat: java.io ...
- MacOS下postgresql数据库密码的那些事
如果你是第一次玩postgresql数据库,你会发现你给role或者user明明设置了密码,但在登录的时候毛都不用输入,直接就进去了,怎么那么爽快!? 虽然爽快,但貌似不该这样啊. 其实这些都和一个重 ...
- React Native 4 for Android源码分析 一《JNI智能指针之介绍篇》
文/ Tamic: http://blog.csdn.net/sk719887916/article/details/53455441 原文:http://blog.csdn.net/eewolf/a ...
- 初识Spring Boot框架(二)之DIY一个Spring Boot的自动配置
在上篇博客初识Spring Boot框架中我们初步见识了SpringBoot的方便之处,很多小伙伴可能也会好奇这个Spring Boot是怎么实现自动配置的,那么今天我就带小伙伴我们自己来实现一个简单 ...
- GC对象分配规则
1.对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC. 2.大对象直接进入老年代(大对象是指需要大量连续内存空间的对象).这样做的目的是避免在Eden区和两个S ...
- Android简易实战教程--第四十话《Spinner》
对于Spinner控件的介绍和使用方法,可以先看之前写过的一篇博客:Spinner控件详解 本篇就基于这个知识点完成一个简单的小案例: 根据介绍,先写一个布局: <?xml version=&q ...
- python命令行参数解析模块argparse和docopt
http://blog.csdn.net/pipisorry/article/details/53046471 还有其他两个模块实现这一功能,getopt(等同于C语言中的getopt())和弃用的o ...
- TensorFlow与OpenCV,读取图片,进行简单操作并显示
TensorFlow与OpenCV,读取图片,进行简单操作并显示 1 OpenCV读入图片,使用tf.Variable初始化为tensor,加载到tensorflow对图片进行转置操作,然后openc ...
- Android Multimedia框架总结(九)Stagefright框架之数据处理及到OMXCodec过程
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52532085 不知不觉到第九篇了,感觉还有 ...