线程局部存储中用到的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. WPF如何设置Image.Source为资源图片

    img.Source = new BitmapImage(new Uri(path,UriKind.RelativeOrAbsolute));

  2. Python 学习第二章

    本章内容 数据类型 数据运算 表达式 if ...else 语句 表达式 for 循环 表达式 while 循环 一.数据类型 在内存中存储的数据可以有多种类型. 在 Python 有五个标准的数据类 ...

  3. 批量拼脚本神器-NimbleText

    工作中要给产品经理写各种脚本拉数据.修改数据.这种批量拼sql,Excel当然是最合适的.但是苦于Excel玩不转,之前一直用Visual Studio Code的多焦点编辑功能,即便如此,这在同事眼 ...

  4. js中两种for循环的使用

    针对两种for循环的使用 1. for in循环的使用环境     可用在字符串.数组.对象中, 需注意:其中遍历对象得到的是每个key  的value值  2. for 变量递加的方式        ...

  5. 【文文殿下】 [USACO08MAR]土地征用 题解

    题解 斜率优化裸题. 有个很玄学的事情,就是我用\(f[i]=min\{f[j-1]+p[j].y*p[i].x\}\) 会很奇怪的Wa . 明明和\(f[i]=min\{f[j]+p[j+1].y* ...

  6. 【dpdk】使用libpcap-PMD驱动收发包

    ref: Dpdk programmer’s guide 1.  概述 dpdk不仅提供针对物理和虚拟网卡的pmd驱动(Poll Mode Drivers),还提供两个纯软件的pmd驱动,libpca ...

  7. Vue + Bootstrap 制作炫酷个人简历(二)

    没想到隔了这么久才来更新. 用vue做简历,不是非常适合,为什么呢. 因为简历没什么数据上的操作,一般都是静态的内容. 不过都说了用Vue来做,也只能强行续命了. 这里是我做好的成品  非一般简历 由 ...

  8. ssm中返回中文字符串时出现乱码?

    问题:返回json格式时,前端ajax请求,响应数据接收正常:     返回String时,响应数据是乱码? 解决:@RequestMapping注解中添加:produces = "text ...

  9. odoo开发笔记 -- 多个视图共用一个模型

    除了写序列优先绑定之外, 窗口引用的视图id也要绑定,否则页面加载的时候,可能不是自己需要显示的视图.例如:<field name="view_id" ref="c ...

  10. Unity使用Rider作为IDE的体验

    Rider 2017.2.1比较完整的支持Unity开发. 通过添加插件代码实现了直接选择Rider作为编辑器. 支持调试. 支持双击跳转代码. Alt+Insert可以插入Unity event函数 ...