每天进步一点点——Linux中的线程局部存储(一)
转载请说明出处:http://blog.csdn.net/cywosp/article/details/26469435
在一个线程中改动了变量中的内容,其它线程都能感知而且能读取已更改过的内容,这对数据交换来说是非常快捷的。可是因为多线程的存在,对于同一个变量可能存在两个或两个以上的线程同一时候改动变量所在的内存内容,同一时候又存在多个线程在变量在改动的时去读取该内存值,假设没有使用相应的同步机制来保护该内存的话,那么所读取到的数据将是不可预知的,甚至可能导致程序崩溃。
Data)或者线程局部存储(TLS: Thread-Local Storage)。这一类型的数据,在程序中每一个线程都会分别维护一份变量的副本(copy)。而且长期存在于该线程中。对此类变量的操作不影响其它线程。
例如以下图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3l3b3Nw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
多线程程序有时有这样的需求:不管创建多少个线程,有些数据的初始化仅仅能发生一次。列如:在C++程序中某个类在整个进程的生命周期内仅仅能存在一个实例对象。在多线程的情况下,为了能让该对象能够安全的初始化。一次性初始化机制就显得尤为重要了。——在设计模式中这样的实现经常被称之为单例模式(Singleton)。
Linux中提供了例如以下函数来实现一次性初始化:
#include <pthread.h>// Returns 0 on success, or a positive error number on errorint pthread_once (pthread_once_t *once_control, void (*init)
(void));利用參数once_control的状态,函数pthread_once()能够确保不管有多少个线程调用多少次该函数,也仅仅会运行一次由init所指向的由调用者定义的函数。init所指向的函数没有不论什么參数,形式例如以下:void init (void){// some variables initializtion in here}
在C++0x以后提供了相似功能的函数std::call_once ()。使用方法与该函数相似。
使用实例请參考https://github.com/ApusApp/Swift/blob/master/swift/base/singleton.hpp实现。
#include <pthread.h>// Returns 0 on success, or a positive error number on errorint pthread_key_create (pthread_key_t *key, void (*destructor)(void *));// Returns 0 on success, or a positive error number on errorint pthread_key_delete (pthread_key_t key);// Returns 0 on success, or a positive error number on errorint pthread_setspecific (pthread_key_t key, const
void *value);// Returns pointer, or NULL if no thread-specific data is associated with keyvoid *pthread_getspecific (pthread_key_t key);
因为全部线程都能够使用返回的新键,所以參数key能够是一个全局变量(在C++多线程编程中一般不使用全局变量。而是使用单独的类对线程局部数据进行封装,每一个变量使用一个独立的pthread_key_t)。destructor所指向的是一个自己定义的函数,其格式例如以下:
void Dest (void *value){// Release storage pointed to by 'value'}
因为系统对每一个进程中pthread_key_t类型的个数是有限制的,所以进程中并不能创建无限个的pthread_key_t变量。Linux中能够通过PTHREAD_KEY_MAX(定义于limits.h文件里)或者系统调用sysconf(_SC_THREAD_KEYS_MAX)来确定当前系统最多支持多少个键。Linux中默认是1024个键。这对于大多数程序来说已经足够了。假设一个线程中有多个线程局部存储变量,通常能够将这些变量封装到一个数据结构中。然后使封装后的数据结构与一个线程局部变量相关联,这样就能降低对键值的使用。
參数value通常指向由调用者分配的一块内存,当线程终止时,会将该指针作为參数传递给与key相关联的destructor函数。当线程被创建时,会将全部的线程局部存储变量初始化为NULL,因此第一次使用此类变量前必须先调用pthread_getspecific()函数来确认是否已经于相应的key相关联,假设没有。那么pthread_getspecific()会分配一块内存并通过pthread_setspecific()函数保存指向该内存块的指针。
參数value的值也能够不是一个指向调用者分配的内存区域。而是不论什么能够强制转换为void*的变量值。在这样的情况下。先前的pthread_key_create()函数应将參数destructor设置为NULL
在使用取出的值前最好是将void*转换成原始数据类型的指针。
- 一个全局(进程级别)的数组,用于存放线程局部存储的键值信息
pthread_key_create()返回的pthread_key_t类型值仅仅是对全局数组的索引,该全局数组标记为pthread_keys。其格式大概例如以下:
数组的每一个元素都是一个包括两个字段的结构,第一个字段标记该数组元素是否在用,第二个字段用于存放针对此键、线程局部存储变的解构函数的一个副本,即destructor函数。
- 每一个线程还包括一个数组,存有为每一个线程分配的线程特有数据块的指针(通过调用pthread_setspecific()函数来存储的指针,即參数中的value)
2. 在常见的存储pthread_setspecific()函数參数value的实现中。大多数都相似于下图的实现。图中假设pthread_keys[1]分配给func1()函数,pthread API为每一个函数维护指向线程局部存储数据块的一个指针数组。当中每一个数组元素都与图线程局部数据键的实现(上图)中的全局pthread_keys中元素一一相应。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3l3b3Nw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
Linux
C++的线程局部存储简单实现可參考https://github.com/ApusApp/Swift/blob/master/swift/base/threadlocal.h,更具体且高效的实现可參考Facebook的folly库中的ThreadLocal实现。更高性能的线程局部存储机制就是使用__thread,这将在下一节中讨论。
每天进步一点点——Linux中的线程局部存储(一)的更多相关文章
- 每天进步一点点——Linux中的线程局部存储(二)
转载请说明出处:http://blog.csdn.net/cywosp/article/details/26876231 在Linux中另一种更为高效的线程局部存储方法,就是使用keyword ...
- linux中的线程局部存储(TLS)
http://blog.csdn.net/cywosp/article/details/26469435
- 在Linux中使用线程
我并不假定你会使用Linux的线程,所以在这里就简单的介绍一下.如果你之前有过多线程方面的编程经验,完全可以忽略本文的内容,因为它非常的初级. 首先说明一下,在Linux编写多线程程序需要包含头文件p ...
- Linux中-POSIX 线程详解
一种支持内存共享的简捷工具 摘自https://www.ibm.com/developerworks/cn/linux/thread/posix_thread1/ 线程是有趣的 了解如何正确运用线 ...
- Linux中epoll+线程池实现高并发
服务器并发模型通常可分为单线程和多线程模型,这里的线程通常是指“I/O线程”,即负责I/O操作,协调分配任务的“管理线程”,而实际的请求和任务通常交由所谓“工作者线程”处理.通常多线程模型下,每个线程 ...
- 每天进步一点点——Linux中的文件描写叙述符与打开文件之间的关系
转载请说明出处:http://blog.csdn.net/cywosp/article/details/38965239 1. 概述 在Linux系统中一切皆能够看成是文件,文件又可分为:普通 ...
- linux中的线程同步:生产者、消费者问题
#include <stdio.h> #include <semaphore.h> #include <unistd.h> #include <stdlib. ...
- Linux查看进程线程个数
1.根据进程号进行查询: # pstree -p 进程号 # top -Hp 进程号 2.根据进程名字进行查询: # pstree -p `ps -e | grep server | awk '{pr ...
- Linux中的task,process, thread 简介
本文的主要目的是介绍在Linux内核中,task,process, thread这3个名字之间的区别和联系.并且和WINDOWS中的相应观念进行比较.如果你已经很清楚了,那么就不用往下看了. LINU ...
随机推荐
- hdu 5185 动态规划 分析降低复杂度
这题说的是 x[1]+x[2]+x[3]+…+x[n]=n, 这里 0 <= x[i] <= n && 1 <= i <= n x[i] <= x[i+1 ...
- 如何使用STM32F4的BootLoader和APP程序
源:如何使用STM32F4的BootLoader和APP程序 STM32 BootLoader升级固件
- GreenOpenPaint的实现(二)打开显示图片
1.DOC中添加,核心图片文件保存在这里.之所以不用Mat,是因为CVVImage有更好的输入输出函数. 我这里直接使用了public public: CvvImage m_image; 2.重载打开 ...
- 20145204 《Java程序设计》第1周学习总结
20145204 <Java程序设计>第1周学习总结 教材学习内容总结 本周经过不断的钻研课本,及看一些老师的视频,我对Java有了一个全新的认知.是的,Java和C都是一种语言,但是Ja ...
- Ubuntu16.04 远程访问RabbitMQ
我们在虚拟机里面安装好RabbitMQ以后,虽然可以在虚拟机中访问,但是在主机端并不能访问 现在要解决这个问题 第一:账户 RabbitMQ为了安全性考虑,默认的guest账户只能在本地127.0.0 ...
- Python学习札记(三十七) 面向对象编程 Object Oriented Program 8 @property
参考:@property NOTE 1.在绑定参数时,为了避免对属性不符合逻辑的操作,需要对传入的参数进行审核. #!/usr/bin/env python3 class MyClass(object ...
- POJ 2771 Guardian of Decency
http://poj.org/problem?id=2771 题意: 一个老师想带几个同学出去,但是他怕他们会谈恋爱,所以带出去的同学两两之间必须满足如下条件之一: ①身高差大于40 ②同性 ③喜欢 ...
- CSS 再学习,文本处理
文本缩进(对p,div有效:对span无效) p {text-indent: 5em;} Tips:一般来说,可以为所有块级元素应用 text-indent,但无法将该属性应用于行内元素(span), ...
- 一定要用Windows自带的记事本编辑 applicationHost.config
访问IIS时,发生了一个 HipIISEngineStub.dll不能读取的问题.(Windows Event可以确认). 很容易在网上找到了对策, http://chrisfleischhacker ...
- git基本操作,一篇文章就够了!
1. git简介 git的通用操作流程如下图(来源于网络) 主要涉及到四个关键点: 工作区:本地电脑存放项目文件的地方,比如learnGitProject文件夹: 暂存区(Index/Stage):在 ...