线程局部存储中用到的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. 分形之谢尔宾斯基(Sierpinski)四面体

    前面讲了谢尔宾斯基三角形,这一节的将对二维三角形扩展到三维,变成四面体.即将一个正四面体不停地拆分,每个正四面体可以拆分成四个小号的正四面体.由二维转变到三维实现起来麻烦了许多.三维的谢尔宾斯基四面体 ...

  2. Cannot retrieve metalink for repository: epel/x86_64. Please verify its path and try again 问题分析

    Cannot retrieve metalink for repository: epel/x86_64. Please verify its path and try again Loaded pl ...

  3. ctags的如何生成tags文件

    tags 在使用vim编程和浏览代码是非常有用.可以用CTRL+]和CTRL+t 来回跳转关键字.先生成自己工作目录的tags.最简单粗暴用法: $cd yourwork $ctags -R * 这样 ...

  4. FFmpeg的安装与使用

    1.概述 FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序.采用LGPL或GPL许可证.它提供了录制.转换以及流化音视频的完整解决方案.它包含了非常先进的音频/视频 ...

  5. 网络流——最大流Dinic算法

    前言 突然发现到了新的一年什么东西好像就都不会了凉凉 算法步骤 建残量网络图 在残量网络图上跑增广路 重复1直到没有增广路(注意一个残量网络图要尽量把价值都用完,不然会浪费建图的时间) 代码实现 #i ...

  6. nginx和tomcat访问图片和静态页面的配置方法

    生产环境下,有时候需要访问图片,正常需要应用ftp.nginx等配套使用,但是有时候为了简化,可以用以下的两种简单的访问,说实话,就是为了偷懒,但是效果是能有的,这就行了,所以今天做这个简化版的方便大 ...

  7. iOS--各种bug详解

    1.为什么传的参数都对,但是就是请求不下来数据. 答:检查下传的字符串中,是不是有多的空格. 例如: 错误:{"startIndex":"1","en ...

  8. 检查iOS项目中是否使用了IDFA

    (1)什么是IDFA 关于IDFA,在提交应用到App Store时,iTunes Connect有如下说明:   这里说到检查项目中是否包含IDFA,那如何来对iOS项目(包括第三方SDK)检查是否 ...

  9. 07-01 Java 封装

    1:成员变量和局部变量的区别 /* 成员变量和局部变量的区别? A:在类中的位置不同 成员变量:在类中方法外 局部变量:在方法定义中或者方法声明上 B:在内存中的位置不同 成员变量:在堆内存 局部变量 ...

  10. js 时间的国际化处理

    //1 获取相对于0时区的当地时区(默认得到的是分钟,可能是负数;北京市东八+8 美国华盛顿为西五-5),中国比美国快13小时 //js默认转换的时候自带时区,只要数据库存的是时间戳,显示的时候不用刻 ...