线程局部存储中用到的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. 通过键盘控制改变物体transform值

    通过键盘控制改变物体transform值 private Vector3 trans; //使用Rotate绕y 轴旋转 transform.Rotate(new Vector3(0,Input.Ge ...

  2. 在线安装WordPress更新 失败的解决办法

    1.  登录ftp登录不上 , 总是登录失败 在服务器上新建了一个vsftpd服务器,并设置了相应的虚拟用户,修改chroot到网站目录 相关连接:https://blog.csdn.net/zhan ...

  3. 【JavaScript】js 中一些需要注意的问题

    关于js中逻辑运算符 sort()方法 1. 关于js中逻辑运算符:|| 和 && 在js逻辑运算中,0."".null.false.undefined.NaN都会 ...

  4. c++中的一些计算的问题

    要实现小数的四舍五入, float a = 3.456; //保留到小数点后两位 float b =(int)((a * 100) + 0.5) / 100.0; 但是这样对负数不好使, 对负数的话, ...

  5. linux 使用进程管理工具 supervisor

    1.supervisor是使用python进行开发的运行在linux服务器上的进程管理工具 老版本的supervisor需要运行在python2环境,如果需要使用supervisor管理python3 ...

  6. react中组件的渲染

    1.封装props对象 2.调用组件函数,得到返回的react元素 3.ReactDom把React元素转成真实的DOM元素并且插入到目标容器内部

  7. skynet inject address file.lua

    inject d test/inject_fuck.lua -- d 是服务的 handle 拿 simpledb.lua 举例,修改如下 local skynet = require "s ...

  8. Swift5 语言指南(二十二) 扩展

    扩展为现有的类,结构,枚举或协议类型添加新功能.这包括扩展您无法访问原始源代码的类型的能力(称为追溯建模).扩展类似于Objective-C中的类别.(与Objective-C类别不同,Swift扩展 ...

  9. Office 2010激活 NO KMS products detected问题

    今天用office2010激活工具Office 2010 Toolkit激活安装的office2010时悲剧的遇到了这个问题,如下图: (这张图是从网上找的,不过和我遇到的问题是一样的). 然后上网搜 ...

  10. main函数中如何等待协程运行完毕

    使用channel同步 package main import ( "fmt" ) func printNumber(num int, c chan struct{}) { fmt ...