线程 TLS
TLS为什么产生呢?是软件开发中的什么问题呢?
TLS 产生背景
进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG。
如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量(被称为static memory local to a thread 线程局部静态变量),就需要新的机制来实现。这就是TLS。
windows和Linux平台下的TLS函数api
linux:
int pthread_key_create(pthread_key_t key, void (*destructor)(void));
int pthread_key_delete(pthread_key_t key);
void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
Windows平台:
每个线程创建时系统给它分配一个LPVOID指针的数组(叫做TLS数组),这个数组从C编程角度是隐藏着的不能直接访问,需要通过一些C API函数调用访问。
首先定义DWORD线程全局变量或函数静态变量,作为线程A访问自己的TLS数组的索引变量。以EasyDarwin中TLS相关代码为例子:
第一步:在线程内调用TlsAlloc()函数,为一个TLS数组索引变量与这个线程的TLS数组的某个槽(slot)关联起来,例如获得一个索引变量:
OSThread::Initialize()中的sThreadStorageIndex = ::TlsAlloc();
sThreadStorageIndex是DWORD类型变量.
第二步,为当前线程动态分配一块内存区域,然后把指向这块内存区域的指针放入TLS数组相应的槽中(使用TlsSetValue()函数调用)。
OSThread::_Entry()函数中TlsSetValue代码:
BOOL theErr = ::TlsSetValue(sThreadStorageIndex, theThread);
theThread是外部传递的函数实参。
第三步,在当前线程的任何函数内,都可以通过TLS数组的索引变量,使用TlsGetValue()函数得到上一步的那块内存区域的指针,然后就可以进行内存区域的读写操作了。这就实现了在一个线程内部这个范围处处可访问的变量。
OSThread* OSThread::GetCurrent()
{
#ifdef __Win32__
return (OSThread *)::TlsGetValue(sThreadStorageIndex);
#elif __PTHREADS__
return (OSThread *)pthread_getspecific(OSThread::gMainKey);
#else
return (OSThread*)cthread_data(cthread_self());
#endif
}
第四步,如果不再需要上述线程局部静态变量,要动态释放掉这块内存区域,然后从TLS数组中放弃对应的槽(使用TlsFree()函数)。
下面是TLS示例代码Demo
#include <stdio.h> // 03UseTLS工程下
#include <windows.h>
#include <process.h>
// 利用TLS跟踪线程的运行时间
DWORD g_tlsUsedTime;
void InitStartTime();
DWORD GetUsedTime();
UINT __stdcall ThreadFunc(LPVOID)
{ int i;
// 初始化开始时间
InitStartTime();
// 模拟长时间工作
i = 10000*10000;
while(i--){}
// 打印出本线程运行的时间
printf(" This thread is coming to end. Thread ID: %-5d, Used Time: %d \n",
::GetCurrentThreadId(), GetUsedTime());
return 0;
}
int main(int argc, char* argv[])
{ UINT uId;
int i;
HANDLE h[10];
// 通过在进程位数组中申请一个索引,初始化线程运行时间记录系统
g_tlsUsedTime = ::TlsAlloc();
// 令十个线程同时运行,并等待它们各自的输出结果
for(i=0; i<10; i++)
{ h[i] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId); }
for(i=0; i<10; i++)
{ ::WaitForSingleObject(h[i], INFINITE);
::CloseHandle(h[i]); }
// 通过释放线程局部存储索引,释放时间记录系统占用的资源
::TlsFree(g_tlsUsedTime);
return 0;
}
// 初始化线程的开始时间
void InitStartTime()
{ // 获得当前时间,将线程的创建时间与线程对象相关联
DWORD dwStart = ::GetTickCount();
::TlsSetValue(g_tlsUsedTime, (LPVOID)dwStart);
}
// 取得一个线程已经运行的时间
DWORD GetUsedTime()
{ // 获得当前时间,返回当前时间和线程创建时间的差值
DWORD dwElapsed = ::GetTickCount();
dwElapsed = dwElapsed - (DWORD)::TlsGetValue(g_tlsUsedTime);
return dwElapsed;
}
实践出真知,多实践多磨砺自己。哈哈
————————————————
版权声明:本文为CSDN博主「codergeek」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/haolipengzhanshen/java/article/details/50814369
线程 TLS的更多相关文章
- 线程局部存储(TLS)
线程局部存储(TLS) 2011-10-11 09:59:28| 分类: Win32---API | 标签:tls |举报 |字号 订阅 什么是线程局部存储 众所周知,线程是执行的单元,同 ...
- 线程本地存储(动态TLS和静态TLS)
线程本地存储(TLS) 对于多线程应用程序,如果线程过于依赖全局变量和静态局部变量就会产生线程安全问题.也就是一个线程的使用全局变量可能会影响到其他也使用此全局变量的线程,有可能会造成一定的错误,这可 ...
- 静态TLS和动态TLS
静态TLS的使用方法: #include <Windows.h> #include <iostream> #include <iomanip> using name ...
- Golang源码学习:监控线程
监控线程是在runtime.main执行的时候在系统栈中创建的,监控线程与普通的工作线程区别在于,监控线程不需要绑定p来运行. 监控线程的创建与启动 简单的调用图 先给出个简单的调用图,好心里有数,逐 ...
- Android深入浅出之Binder机制
一说明 Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Service就是通过Binder机制来和客户端通讯交互的.所以搞明白Binder的话,在很大程度上就能理解程序运行 ...
- 多线程中Local Store Slot(本地存储槽)
在Java中有一种ThreadLocal机制,为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突.从线程的角度看,就好像每一个线程都完全 ...
- minidump详细介绍
Effective minidump 简介 在过去几年里,崩溃转储(crash dump)成为了调试工作的一个重要部分.如果软件在客户现场或者测试实验室发生故障,最有价值的解决方式是能够创建一个故障瞬 ...
- handler原理
一.消息机制概述 1.消息机制的简介 (1)Handler是什么 handler使Android给我们提供的用来更新UI的一套机制,也是一套消息处理机制:我们可以用它发送处理消息. (2)Androi ...
- Minidump文件分析
原文地址:blog.csdn.net/pkrobbie/article/details/6636310 简介 在过去几年里,崩溃转储(crash dump)成为了调试工作的一个重要部分.如果软件在客户 ...
随机推荐
- 洛谷 P5071 - [Ynoi2015] 此时此刻的光辉(莫队)
洛谷题面传送门 一道其实算得上常规的题,写这篇题解是为了总结一些数论中轻微(?)优化复杂度的技巧. 首先感性理解可以发现该问题强于区间数颜色问题,无法用常用的 log 数据结构维护,因此考虑分块/莫队 ...
- HDU 6755 - Fibonacci Sum(二项式定理+推式子)
题面传送门 其实是一道还好的题罢,虽然做了我 2147483647(bushi,其实是 1.5h),估计也只是因为 HDU 不支持数据下载所以错误总 debug 出来 首先看到 \(10^9+9\) ...
- 【GS模型】使用R包sommer进行基因组选择的GBLUP和RRBLUP分析?
目录 简介 GS示例代码 简介 R包sommer内置了C++,运算速度还是比较快的,功能也很丰富,可求解各种复杂模型.语法相比于lme4包也要好懂一些. 建议查看文档:vignette("v ...
- expr计算字符串长度
命令:expr length "quanzhiqinag" #!/bin/bash for N in quan zhi qiang do if [ `expr length $N ...
- zabbix_get
zabbix_get是一个命令行工具,可用于从一个远程的Zabbix探针获取监控数据. 1.2 用法 zabbix_get [-hV] [-s <host name or IP> ] [- ...
- 分布式服务治理框架Dubbo的前世今生及应用实战
Dubbo的出现背景 Dubbo从开源到现在,已经出现了接近10年时间,在国内各大企业被广泛应用. 它到底有什么魔力值得大家去追捧呢?本篇文章给大家做一个详细的说明. 大规模服务化对于服务治理的要求 ...
- 【Maven实战技巧】「插件使用专题」Maven-Archetype插件创建自定义maven项目骨架
技术推荐 自定义Archetype Maven骨架/以当前项目为模板创建maven骨架,可以参考http://maven.apache.org/archetype/maven-archetype-pl ...
- centos服务器上挂载exFat U盘
有些场景,我们需要在服务器上插入U盘,但是现在的U盘或者移动硬盘,大多都是exFat格式的,有时候linux系统识别不了,可以按照以下方式挂载. 1.安装nux repo(可以不装) yum inst ...
- 前端页面存放token
//本地缓存,记录token function set(type, value) { localStorage.setItem(type, value); } function get(type) { ...
- 容器的分类与各种测试(三)——deque
deque是双端队列,其表象看起来是可以双端扩充,但实际上是通过内存映射管理来营造可以双端扩充的假象,如图所示 比如,用户将最左端的buff用光时,map会自动向左扩充,继续申请并映射一个新的buff ...