线程的局部存储tls (thread local storage)

本文深入探讨了线程局部存储的概念,并详细介绍了静态TLS与动态TLS的区别及其实现方式,通过示例代码展示两者在多线程环境下的表现差异。
线程局部存储tls(thread local storage)
线程的局部存储就是让一个全局的变量或者静态变量在每一个线程都有自己的立足之地.
动态TLS
首先调用API函数,获取当前进程可用的TLS索引,成功获取之后,我们可以通过Tls索引来进行数据的存取,线程局部存储有动态Tls,也有静态的Tls,内部的实现原理其实是一样的.
静态的Tls

使用比较方便只是声明时使用 _declspec(thread) 加在声明之前,如普通变量 int g_int=0;  Tls变量声明 _declspec(thread) int g_int=0;使用和普通变量是一样的,tls变量在每个线程的上下文有自己的内存块,用来放置数据.

下边草图用来帮助理解,并非完全就这样:


#include "stdafx.h"
#include <windows.h>

_declspec(thread) int g_int=0;  //静态tls变量
int g_long=0;//普通变量
DWORD WINAPI Fun1(LPVOID param);
DWORD WINAPI Fun2(LPVOID param);
int _tmain(int argc, _TCHAR* argv[])
{
	DWORD nThredId;
	HANDLE hThread[2]={0};
	hThread[0]=CreateThread(NULL,0,Fun1,(LPVOID)NULL,0,&nThredId);
	hThread[1]=CreateThread(NULL,0,Fun2,(LPVOID)NULL,0,&nThredId);
	WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
	CloseHandle(hThread[0]);
	CloseHandle(hThread[1]);
	return 0;
}

DWORD WINAPI Fun1( LPVOID param )
{
	while(1)
	{
		g_int+=1;
		g_long+=1;
		printf("线程1: g_int=%d   g_long=%d\t",g_int,g_long);	
		Sleep(1000);
	}
	return 0;
}

DWORD WINAPI Fun2( LPVOID param )
{	
	while(1){
		Sleep(1000);
		printf("线程2: g_int=%d  g_long=%d\n",g_int,g_long);
	}
	return 0;
}
以上代码是用的是线程的静态TLS技术,运行结果如下:

线程1: g_int=8   g_long=8       线程2: g_int=0  g_long=8
线程1: g_int=9   g_long=9       线程2: g_int=0  g_long=9
线程1: g_int=10   g_long=10     线程2: g_int=0  g_long=10
线程1: g_int=11   g_long=11     线程2: g_int=0  g_long=11
线程1: g_int=12   g_long=12     线程2: g_int=0  g_long=12
线程1: g_int=13   g_long=13     线程2: g_int=0  g_long=13
线程1: g_int=14   g_long=14     线程2: g_int=0  g_long=14
线程1: g_int=15   g_long=15     线程2: g_int=0  g_long=15
线程1: g_int=16   g_long=16     线程2: g_int=0  g_long=16
线程1: g_int=17   g_long=17     线程2: g_int=0  g_long=17
线程1: g_int=18   g_long=18     线程2: g_int=0  g_long=18

可以看出,没有使用tls的变量,在线程1修改值,线程2的输出也会受影响,而是用Tls的变量,在每个线程的上下文有自己的存储空间,一个线程做修改,不影响另一个线程.

下边是将上述代码改为动态Tls来实现,具体如下:

#include "stdafx.h"
#include <windows.h>

DWORD nTlsIndex=0;  //TLS索引
int g_long=0;  //普通变量
DWORD WINAPI Fun1(LPVOID param);
DWORD WINAPI Fun2(LPVOID param);
int _tmain(int argc, _TCHAR* argv[])
{
	//TLS索引是属于进程的,而同一个TLS在不同的线程中都有自己的内存块.所以使用TlsGetValue(nTlsIndex)获取值时,返回的是线程各自的内存块的指针.
	//第一步 分配TLS索引(向该进程索要可用的TLS索引)
	nTlsIndex=TlsAlloc();
	DWORD nThredId;
	HANDLE hThread[2]={0};
	hThread[0]=CreateThread(NULL,0,Fun1,(LPVOID)NULL,0,&nThredId);
	hThread[1]=CreateThread(NULL,0,Fun2,(LPVOID)NULL,0,&nThredId);
	WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
	CloseHandle(hThread[0]);
	CloseHandle(hThread[1]);
	//最后要释放TLS
	TlsFree(nTlsIndex);
	return 0;
}

DWORD WINAPI Fun1( LPVOID param )
{
	int i=1;
	while(1)
	{
		//设置TLS索引在该线程上对应的地址空间的值
		TlsSetValue(nTlsIndex,(LPVOID)i++);
		g_long+=1;
		printf("线程1: g_int=%d   g_long=%d\t",(INT)TlsGetValue(nTlsIndex),g_long);	
		Sleep(1000);
	}
	return 0;
}

DWORD WINAPI Fun2( LPVOID param )
{	
	while(1){
		Sleep(1000);
		//设置TLS索引在该线程上对应的地址空间的值
		TlsSetValue(nTlsIndex,(LPVOID)1);
		//TlsGetValue(nTlsIndex) 此函数通过TLS索引来获取在线程上下文中对应的值
		printf("线程2: g_int=%d  g_long=%d\n",(INT)TlsGetValue(nTlsIndex),g_long);
	}
	return 0;
}
通过上边的比较静态TLS是用起来要比动态TLS是用起来方便的多,在实际应用中,还要根据实际情况选择.




评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值