线程局部存储中用到的API基础:(TLS:Thread Local Storage)
1、在主线程中申请索引
g_index=::TlsAlloc();
2、在线程函数中使用索引
存值:::TlsSetValue(g_index,(LPVOID)value); value是要存入此线程私有空间的值;
取值: ::m_value=::TlsGetValue(g_index);
==================================================================
框架程序利用上面API基础封装了一套功能更加完善的线程局部存储类,之后会利用这个类实现框架中的“运行时类信息”、“线程状态”、“模块状态”、“模块线程状态”。
首先对于每个线程来说,其线程私有的数据空间数据结构如下:
struct CThreadData:public CNoTrackObject //(不必跟踪内存的使用)
{
CThreadData* pNext; //指向下一个线程私有数据空间
int nCount; //pData数组元素个数
LPVOID* pData; //实际存储数据的数组
}
CNoTrackObject类重写了new 操作符和delete操作符 减少了额外内存的使用,系统要求线程私有数据都应该从CNoTrackObject类继承。
为了管理线程私有数据空间,将上述各个线程的CThreadData结构连成一个表,使用模板类CTypedSimpleList管理它,
此时线程私有的数据空间数据结构可以是任何从CNoTrackObject类继承来的结构体(但结构体中的基本属性不变)。
例如:
struct MyThreadData:CNoTrackObject
{
MyThreadData * pNext;
int someData
}
CTypedSimpleList<MyThreadData *> list;
CTypedSimpleList提供了对线程局部数据空间链表的的“增”“删”“查” ,它只提供对(MyThreadData)整体操作,改动里面的数据需要使用下面介绍的。
使用下面介绍的会利用CTypedSimpleList来访问其指定的线程的私有数据空间。
注意:链表首个元素的指针是void* 类型的 所以无法通过MyThreadData中的pNext指针访问下一个MyThreadData结构。因此创建CTypedSimpleList对象后需要调用
Construct(int n_NextOffset)设置next指针的偏移量,访问下一个MyThreadData都是通过n_NextOffset偏移量。(不懂为什么这样弄)
==============================================================
此时需要一个数组来记录CThreadData中pData数组成员的使用情况 数组元素结构如下(数组下标表示pData数组对应下标的使用情况)
struct CSlotData
{
DWORD dwFlags; //pData数组中项的使用标志(分配/未分配)
HINSTANCE hInst; //占用此数组项的模块
}
============================================================
之后通过CThreadSlotData类来管理整个线程局部存储的数据结构
构造函数CThreadSlotData()
初始化CTypedSimpleList对象设置偏移量;
初始化CSlotData数组未NULL;
使用API g_index=::TlsAlloc()申请一个索引;(之后会把CThreadData结构存入g_index索引)
分配可以使用的槽号int AllocSlot() 返回槽号index
首先查看上次分配的槽号的下一个槽是否分配;
如果未分配直接分配。(更新部分维护数据)
否则遍历CSlotData数组查找未分配的槽;
如果不存在空槽则申请更多的空间,返回新申请的首个槽。
设置某个槽的数据指针 SetValue(int nSlot,void* pValue)
首先利用API m_value=::TlsGetValue(g_index)返回私有数据空间指针,强转为CThreadData*
如果指针为NULL(首次使用线程私有数据)
则新建CThreadData并且初始化然后加入CThreadData链表(即CTypedSimpleList)为CThreadData*中pData申请m_nMax个内存空间
如果只是nSlot大于CThreadData中nCount(超出)则为CThreadData*中pData同样申请m_nMax个内存空间(m_nMax在分配nslot时肯定被增大了)
将新申请的内存初始化为0;
将CThreadData结构体 设回::TlsSetValue(g_index,(LPVOID)value);
设置结构体CThreadData中pData数组中的第nSlot项的值。
取得某个槽的值 GetThreadValue(int nSlot) 返回槽值
直接取::m_value=::TlsGetValue(g_index);强转为CThreadData*;
取得里面的值。如果取不到返回NULL。
删除某个槽的数据 FreeSlot(int nSlot)
遍历CThreadData链表依次删除nSlot槽中的数据
删除线程私有数据空间 DeleteValues(HINSTANCE hInst, BOOL bAll) / DeleteValues(CThreadData* pData, HINSTANCE hInst)
DeleteValues(HINSTANCE hInst, BOOL bAll) //删除所有或一个(调用了DeleteValues(CThreadData* pData, HINSTANCE hInst))
DeleteValues(CThreadData* pData, HINSTANCE hInst) //删除一个
================================================================
上面所实现的类是基于 CThreadData的线程局部存储结构,其中每个槽内存储的是指针并没有为这个指针分配内存空间,
现在想让槽中可以存用户自定义的任何数据类型并为其分配内存空间。
class CThreadLocalObject 类就是为此服务的。
主要函数GetData()参数为要存放的数据的构造函数(用来创建一个数据项)
首先取得分配槽 然后取得槽中的数据指针。
如果指针为空,则调用参数创建一个指定类型的变量。
================================================================
最后的接口
template<class TYPE>
class CThreadLocal::public CThreadLocalObject
{
public:
TYPE* GetData()
{
TYPE* pData=(TYPE*)CThreadLocalObject::GetData(&CreateObject);
return pData;
}
TYPE* GetDataNA()
{
TYPE* pData=(TYPE*)CThreadLocalObject::GetDataNA();
return pData;
}
operator TYPE *() { return GetData();}
TYPE * operator-> () { return GetData();}
public:
static LPVOID CreateObject() {return new TYPE;}
}
此类重载了运算符 “*”和“->”
当你创建类对象时:
Struct CmyData:public CNoTrackObject
{
int nNumber;
}
CThreadLocal<CmyData> m_Test;
m_Test->nNumber=20; //申请一个槽并且存入CmyData类型指针,并且将其成员nNumber=20
m_Test直接会被当做CmyData结构指针来使用。
=================================================
完毕