线程局部存储中用到的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结构指针来使用。

=================================================

完毕

MFC框架之线程局部存储的更多相关文章

  1. MFC学习-第2,3课 MFC框架的运行机制

    转自:http://blog.163.com/zhigang0633@126/blog/static/38790491200822711526168/ 讲述MFC AppWizard的原理与MFC程序 ...

  2. Delphi管理多线程之线程局部存储:threadvar

    尽管多线程能够解决许多问题,但是同时它又给我们带来了很多的问题.其中主要的问题就是:对全局变量或句柄这样的全局资源如何访问?另外,当必须确保一个线程中的某些事件要在另一个线程中的其他时间之前(或之后) ...

  3. 【windows核心编程】线程局部存储TLS

    线程局部存储TLS, Thread Local Storage TLS是C/C++运行库的一部分,而非操作系统的一部分. 分为动态TSL 和 静态TLS 一.动态TLS 应用程序通过调用一组4个函数来 ...

  4. 在MFC框架中使用OpenGL的简单实例

    引言 我们知道,在MFC框架中,用于绘图的接口是GDI.但GDI只能绘制简单的2D图形,要想制作精美的3D图形,一个可行的办法是使用OpenGL或者Direct3D等第三方库. 由于最近在给导师的一个 ...

  5. MFC框架

    第一点:类别型录网的搭建: 类别型录网搭建的目的是为了实现所谓的"执行期类型识别",也就是在程序运行的时候识别出某个对象是否是某个类的实例(基类也可以).这里还不是很明白为什么需要 ...

  6. MFC框架中消失的WinMain()

    学过一段时间的MFC之后,很多人大概都有一个疑问:在MFC中,WinMain()哪去了?因为任何一个使用过Win32 SDK编程的人都知道,WinMain()函数是Win32程序开始的入口点,可是在M ...

  7. 对MFC 框架的认识

    1.MFC 的概念 微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是一个微软公司提供的类库(class libraries),以C++类的形式封装了Wind ...

  8. MFC多线程各种线程用法 .

    http://blog.csdn.net/qq61394323/article/details/9328301 一.问题的提出 编写一个耗时的单线程程序: 新建一个基于对话框的应用程序SingleTh ...

  9. PE格式第八讲,TLS表(线程局部存储)

    PE格式第八讲,TLS表(线程局部存储) 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶复习线程相关知识 首先讲解 ...

随机推荐

  1. lnmp mysql添加用户命令

    cd /usr/local/mysql/bin/grant all privileges on *.* to 'root'@'%' identified by '12345678';flush pri ...

  2. SharePoint 列表中增加列编辑功能菜单

    需求描述 在企业的部署中,经常将SharePoint和TFS集成在一起,两个系统之间相互读取数据,展现开发进度.在TFS 2018之前版本中,由于TFS的门户定制功能有限,用户比较喜欢使用ShareP ...

  3. Spark技术的总结 以及同storm,Flink技术的对比

    spark总结 1.Spark的特点: 高可伸缩性 高容错 基于内存计算 支持多种语言:java,scala,python,R 高质量的算法,比MapReduce快100倍 多种调度引擎:可以运行于Y ...

  4. 【BZOJ3551】 [ONTAK2010]Peaks加强版

    BZOJ3551 [ONTAK2010]Peaks加强版 Solution Kruscal重构树后发现可以对于小于的离散化然后倍增+主席树找到上一个的可行解. 然后就可以了. 如果数组开的不好,容易在 ...

  5. 牛客第二场 C.message(计算几何+二分)

    题目传送:https://www.nowcoder.com/acm/contest/140/C 题意:有n个云层,每个云层可以表示为y=ax+b.每个飞机的航线可以表示为时间x时,坐标为(x,cx+d ...

  6. 文件寄生——寻找宿主的不归路(NTFS文件流实际应用)

    咱们今天来研究下NTFS文件流: NTFS文件系统实现了多文件流特性,NTFS环境一个文件默认使用的是未命名的文件流,同时可创建其他命名的文件流,windows资源管理器默认不显示出文件的命名文件流, ...

  7. Weblogic 错误 <BEA-000403> <BEA-000438>解决办法

      控制台提示如下错误: <Error> <Socket> <BEA-000438> <Unable to load performance pack. Us ...

  8. [JavaScript] Nginx实现跨域设置

    假如跨域请求的接口为:http://xxx.cn/was5/web/search Nginx配置: 在conf/nginx.conf文件中 location / { root html; index ...

  9. Windows 网卡超过序列

    以前连接过别的网络,现在重新装网已经排到"网络连接9"了,连以前起过的wifi名称(ssid)都变成"*** 3"了,怎么把以前的清空呢? 这是解决办法: wi ...

  10. [Leetcode]316.去除重复字母

    题目 贪心方法 用一个两个数组vector<int>cnt,vector<bool>in_right_place: string res:目前符合条件的字符串,到代码结束的时候 ...