线程局部存储tls的使用
线程局部存储(Thread Local Storage,TLS)主要用于在多线程中,存储和维护一些线程相关的数据,存储的数据会被关联到当前线程中去,并不需要锁来维护。。
因此也没有多线程间资源竞争问题,那如何去实现TLS存储呢,主要有以下几种方式:
- gcc和clang的
__thread修饰符 - windows下msvc的
__declspec(thread)修饰符 - pthread库
pthread_setspecific和pthread_getspecific接口 - windows下的
TlsSetValue和TlsGetValue
__thread和__declspec(thread)的使用
其中__thread和__declspec(thread)用起来最为方便,只需要在static或者全局变量前加上此修饰符,然后在线程里面访问变量就行了
例如:
tb_void_t tb_thread_func(tb_cpointer_t priv)
{
// 定义一个线程局部变量
static __thread int a = 0;
// 初始化这个变量,设置为当前线程id
if (!a) a = tb_thread_self();
}
如果运行多个线程的话,上述代码中每个线程的变量a的值,都是不相同的,值为每个线程的id
而 __declspec(thread) 用起来也是类似,只需替换下 __thread就行了
虽然这两个修饰符用起来很方便,但是需要编译器支持,虽然现在大部分平台的编译器都已支持,但是作为跨平台开发,这样还是不够的
毕竟还是有不少低版本gcc,不一定支持__thread,尤其是嵌入式开发领域,交叉编译工具链中的编译器支持力度差异还是蛮大的。。
另外,使用__thread进行tls数据维护,需要手动管理相关内存的释放问题,用的不好很容易导致内存泄露。。
pthread接口
pthread 的 tls 相关接口,比较完善,并且支持注册free函数,在线程退出的时候,自动释放相关tls数据,避免内存泄露,但是使用上稍显复杂了些
我们看个简单的例子:
// 测试线程中tls变量存储的key,需定义为全局或者static
static pthread_key_t g_local_key = 0;
static tb_void_t tb_thread_local_free(tb_pointer_t priv)
{
tb_trace_i("thread[%lx]: free: %p", tb_thread_self(), priv);
}
static tb_void_t tb_thread_local_init(tb_void_t)
{
// 创建tls的key,并且设置自动释放函数
pthread_key_create(&g_local_key, tb_thread_local_free);
}
static tb_int_t tb_thread_local_test(tb_cpointer_t priv)
{
// 在所有线程中,仅执行一次,用于在线程内部初始化 tls 的 key
static pthread_once_t s_once = PTHREAD_ONCE_INIT;
pthread_once(&s_once, tb_thread_local_init);
// 尝试读取当前tls数据
tb_size_t local;
if (!(local = (tb_size_t)pthread_getspecific(g_local_key)))
{
// 设置tls数据为当前线程id
tb_size_t self = tb_thread_self();
if (0 == pthread_setspecific(g_local_key, (tb_pointer_t)self))
local = self;
}
return 0;
}
看上去复杂了些,但是更加灵活,如果不需要在线程内部创建key的话,就不需要调用pthread_once了,直接把创建好的key传入线程内部去访问就好。
TlsSetValue 接口
此套接口(TlsSetValue, TlsGetValue, TlsAlloc, TlsFree),属于windows的tls操作接口,当然不能跨平台了,使用起来和pthread的差不多,但是无法注册自动释放函数,并且也没提供类似pthread_once的接口
在线程内部自创建key,功能上稍显不足。。
static tb_int_t tb_thread_local_test(tb_cpointer_t priv)
{
// 创建一个tls的key,注:此处非线程安全,最好放到类似pthread_once提供的init函数中去创建
// 此处就临时先这么写了,仅仅只是为了方便描述api用法,不要照搬哦。。
static DWORD s_key = 0;
if (!s_key) s_key = TlsAlloc();
// 尝试读取当前tls数据
DWORD local;
if (!(local = TlsGetValue(s_key)))
{
// 设置tls数据为当前线程id
tb_size_t self = tb_thread_self();
if (TlsSetValue(s_key, (LPVOID)self))
local = self;
}
return 0;
}
其实windows上还提供了FlsAlloc, FlsSetValue系列接口,给协程使用,并且支持注册自动释放的回调函数,不过对系统版本有些要求,像xp这些老系统就用不了了。。
这里就不多描述了。
tbox提供的thread_local接口封装
最近对tbox的tls接口进行了改造,并且重构了实现逻辑,在剪口易用性、功能性以及效率上都得到了很大的提升。。
目前支持以下功能:
- 支持注册自动释放回调,保证在线程退出时,自动释放设置的tls数据
- 支持在线程内部进行线程安全的key创建
- tbox退出时会自动销毁所有创建的key,当然也可以提前主动销毁它
用起来也很方便,很pthread很类似,但是内部自动调用了pthread_once,不用想pthread那样显式的去调用它了,例如:
static tb_void_t tb_demo_thread_local_free(tb_cpointer_t priv)
{
tb_trace_i("thread[%lx]: free: %p", tb_thread_self(), priv);
}
static tb_int_t tb_demo_thread_local_test(tb_cpointer_t priv)
{
/* 线程安全地初始化一个tls对象,相当于key,并且注册自动free回调
*
* 注:虽然所有线程都会执行到这个tb_thread_local_init
* 但是s_local的tls对象,只会确保初始化一次,内部有类似pthread_once接口来维护
*/
static tb_thread_local_t s_local = TB_THREAD_LOCAL_INIT;
if (!tb_thread_local_init(&s_local, tb_demo_thread_local_free)) return -1;
// 尝试读取当前tls数据
tb_size_t local;
if (!(local = (tb_size_t)tb_thread_local_get(&s_local)))
{
// 设置tls数据为当前线程id
tb_size_t self = tb_thread_self();
if (tb_thread_local_set(&s_local, (tb_cpointer_t)self))
local = self;
}
return 0;
}
在线程退出时,它会自动调用free回调,释放对应残留的tls数据,并且在tb_exit退出后,销毁所有创建的tls对象
当然你可以可以主动调用:tb_thread_local_exit(&s_local) 来销毁它。。
tbox的这套接口,相比pthread减少了一个init的回调函数,比windows那套多了自动释放的机制,并且同时支持跨平台。。
杂谈
之前我看到一些库中,对__thread和pthread接口进行了混用,感到很是莫名,个人感觉是有问题的,例如:
static __thread pthread_key_t g_key;
原本pthread文档中就明确表述key需要全局或者static存储,而这里加上__thread后,其实每个线程访问的key都不是同一个key了哦。。
总结
如果仅仅只是想用来在线程内部存储一些简单的int数据,并且不考虑完备的跨平台支持,那么建议直接使用__thread或者__declspec(thread)j就行了,非常方便易用
如果要考虑跨平台操作的话,tbox的tls接口也是个不错的选择哦。。
个人主页:TBOOX开源工程
原文出处:http://tboox.org/cn/2016/09/28/thread-local/
线程局部存储tls的使用的更多相关文章
- 【windows核心编程】线程局部存储TLS
线程局部存储TLS, Thread Local Storage TLS是C/C++运行库的一部分,而非操作系统的一部分. 分为动态TSL 和 静态TLS 一.动态TLS 应用程序通过调用一组4个函数来 ...
- 线程局部存储TLS
1 .使用线程局部存储的理由 当我们希望这个进程的全局变量变为线程私有时,而不是所有线程共享的,也就是每个线程拥有一份副本时,这时候就可以用到线程局部存储(TLS,Thread Local Stora ...
- 线程局部存储 TLS
C/C++运行库提供了TLS(线程局部存储),在多线程还未产生时,可以将数据与正在执行的线程关联.strtok()函数就是一个很好的例子.与它一起的还有strtok_s(),_tcstok_s()等等 ...
- 线程局部存储TLS(thread local storage)
同一全局变量或者静态变量每个线程访问的是同一变量,多个线程同时访存同一全局变量或者静态变量时会导致冲突,尤其是多个线程同时需要修改这一变量时,通过TLS机制,为每一个使用该全局变量的线程都提供一个变量 ...
- linux中的线程局部存储(TLS)
http://blog.csdn.net/cywosp/article/details/26469435
- PE格式第八讲,TLS表(线程局部存储)
PE格式第八讲,TLS表(线程局部存储) 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶复习线程相关知识 首先讲解 ...
- 线程局部存储(TLS)
线程局部存储(TLS) 2011-10-11 09:59:28| 分类: Win32---API | 标签:tls |举报 |字号 订阅 什么是线程局部存储 众所周知,线程是执行的单元,同 ...
- 【C# 线程】线程局部存储(TLS)理论部分 ThreadStatic|LocalDataStoreSlot|ThreadLocal<T>
线程本地存储(TLS:Thread Local Storage) 线程本地存储(Thread Local Storage),字面意思就是专属某个线程的存储空间.变量大体上分为全局变量和局部变量,一个进 ...
- 基于TLS(线程局部存储)的高效timelog实现
什么是timelog? 我们在分析程序性能的时候,会加入的一些logging信息记录每一部分的时间信息 timelog模块的功能就是提供统一的接口来允许添加和保存logging 我们正在用的timel ...
随机推荐
- Mysql 数据库存储的原理??
储存过程是一个可编程的函数,它在数据库中创建并保存.它可以有 SQL 语句和一些特殊的控制结 构组成.当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的.数据库中的 ...
- 生日蛋糕 (poj1190) (dfs剪枝)
[题目描述] 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为 ...
- #10017 传送带(SCOI 2010)(三分套三分)
[题目描述] 在一个 2 维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段 AB 和线段 CD.lxhgww 在 AB上的移动速度为 P ,在 CD 上的移动速度为 Q,在平 ...
- SCUT - 31 - 清一色 - dfs
https://scut.online/p/31 还是不知道为什么RE了.的确非常玄学. 重构之后就没问题了.果然写的越复杂,分的情况越乱就越容易找不到bug. #include<bits/st ...
- [ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式
原文:[ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式 IoC主要体现了这样一种设计思想:通过将一组通用流程的控制权从应用转移到框架之中以实现对流程的复用,并按照“好莱坞法则”实现应用 ...
- 利用vsftpd在Linux构建安全的FTP服务
最近在机房搭建Linux环境,需要用到了FTP服务,查看了许多的资料,在这里做一下笔记 一.安装 方法一,使用yum命令安装,需要能够连接外网 # yum install vsftpd 方法二,使用安 ...
- vue.js(19)--vue中子组件调用父组件的方法
子组件是不能直接使用父组件中的方法的,需要进行事件绑定(v-on:自定义方法名="父组件方法名"),然后在子组件中再定义一个方法,使用this.$emit('自定义方法名')语句完 ...
- 团队中的 Git 实践
转载自:https://segmentfault.com/a/1190000004963641 本文首发于欧雷流.由于我会时不时对文章进行补充.修正和润色,为了保证所看到的是最新版本,请阅读原文. 在 ...
- Python + logging输出到屏幕,将log日志写入到文件
logging提供了一组便利的函数,用来做简单的日志.它们是 debug(). info(). warning(). error() 和 critical(). logging函数根据它们用来跟踪的事 ...
- MongoDB Compass管理工具下载、安装和使用
内容来自:https://jingyan.baidu.com/article/925f8cb884f6f8c0dce0565a.html ,https://blog.csdn.net/bg101775 ...