MFC中的TLS

Windows操作系统提供了Process/Thread的程序模型,其中Process是资源的分配对象
,掌握了程序所拥有的资源,而Thread则代表了程序的运行,是操作系统调度的对象。需
要注意,操作系统中,这两种东西都是一种KERNEL32对象。分别由Process DataBase和Th
read DataBase来表示。具体可以参考Matt Petrik的Windows 95 Programing Secret。

    Thread Local Storage是一个实现Thread的全局数据的机制,并且这些数据仅仅在这
个Thread中可见,因为这些数据保存在该Thread的Thread DataBase中:在每一个Thread
DataBase中都定义了一个64元的DWORD数组用来保存这些数据。同时操作系统也提供了相应
的函数来完成对这些数据的操作,如:TlsAlloc,TlsFree,TlsSetValue,TlsGetValue。

    在MFC中,也提供了TLS功能,为此MFC设计了一系列的类和程序来完成这个任务。具体
的程序在afxtls.cpp和afxtls_.h中。
涉及到的主要的类有:
class CTypedSimpleList : public CSimpleList
struct CThreadData : public CNoTrackObject
struct CSlotData
class CThreadSlotData
class CThreadLocal : public CThreadLocalObject
    其中CThreadSlotData是封装TLS的最重要的类,CTypedSimpleList,CSlotData,CTh
readDAta都是为了封装TLS而设计的只具有辅助功能的类。CThreadLocal是更高层的封装。

    首先让我们来对其数据封装方式进行分析,重要的类的定义及其分析如下所示:(为简
单起见,只列出数据成员而不再列出函数成员)
定义:
class CThreadSlotData
{
public:
DWORD m_tlsIndex;
int m_nAlloc;   
int m_nRover; 
int m_nMax;   
CSlotData* m_pSlotData;
CTypedSimpleList<CThreadData*> m_list;
CRITICAL_SECTION m_sect;
};
分析:
    在afxtls.cpp中定义了一个CThreadSlotData类的全局变量:_afxThreadData。在CTh
readLocal的成员函数中大量使用了这个全局变量来访问TLS功能。
DWORD m_tlsIndex
用来保存TLS数据的索引,也就是在Thread DataBase中64元数组中的偏移量,这个数据在
CThreadSlotData类的构造函数中初始化。
int m_nAlloc
int m_nRover
int m_nMax
这三个变量用来分配slot和记录相关状态,比如m_nAlloc用来保存当前已经分配的slot的
个数。线程为每一个TLS数据分配一个slot。
CSlotData* m_pSlotData;
用来记录已经分配的每一个slot的状态:已经使用或是尚未使用。
CTypedSimpleList<CThreadData*> m_list;
CThreadSlotData为每一个Thread实现一个并且只实现一个CThreadData对象,并且用链表
类对象m_list来管理它们。实际上,真正被保存到Thread DataBase中去的是这个CThread
Data对象的指针,而程序员要保存的TLS数据被保存到这个CThreadData对象的pData成员指
向的动态数组中。所有Thread的CThreadData对象通过CThreadData对象的pNext成员连成链
表,并由CTypedSimpleList<CThreadData*> m_list管理。
CRITICAL_SECTION m_sect;
由于所有Thread的TLS操作都要靠访问_afxThreadData来实现,这样就产成了多线程同步的
问题,m_sect就是用来进行线程同步的变量。保证每次只有一个Thread在访问_afxThread
Data中的成员变量。

定义:
struct CThreadData : public CNoTrackObject
{
CThreadData* pNext; // required to be member of CSimpleList
int nCount;         // current size of pData
LPVOID* pData;      // actual thread local data (indexed by nSlot)
};
分析:
    CThreadData用来辅助CThreadSlotData来完成TLS功能。每一个Thread的TLS数据都要
靠一个CThreadData对象来管理和保存。
CThreadData* pNext
在CThreadSlotData中,CThreadData由一个链表来管理,pNext用来把各个Thread的CThre
adData对象连成链表。
int nCount
指出用于保存TLS数据指针的动态数组的长度。
LPVOID* pData
在CThreadData保存的实际上是各个TLS数据的指针,为此定义了一个指针数组,nCount用
来指示数组长度,pData用来指出数组的基地址。

定义:
struct CSlotData
{
DWORD dwFlags;      // slot flags (allocated/not allocated)
HINSTANCE hInst;    // module which owns this slot
};
分析:
    CSlotData用来辅助CThreadSlotData来完成TLS功能。每一个Thread的TLS数据都要靠
一个CThreadData对象来保存,具体实现是把TLS数据的指针保存在CThreadData对象的动态
指针数组中(基地址由pData指出)。而这个数组中每一个成员的使用状况则由一个与之长度
相同的CSlotData数组来表示,具体由DWORD dwFlags来表明。

    从上面的分析不难发现,MFC中TLS功能的封装是这样的,所有Thread的TLS数据指针都
保存在一个动态的指针数组中,而该数组的基地址由一个CThreadData对象的 pData指出。
同时,保存在Thread DataBase中的是这个CThreadData对象的指针,而不是TLS数据的指针
,并且其索引值均相同,都为CThreadSlotData类中的m_tlsIndex成员。而且,在CThread
SlotData中提供了一个链表来管理所有Thread的CThreadData对象。这样CThreadSlotData
类就能访问所有的Thread的TLS数据。见图tls.bmp。(为了方便,我把图放到了签名档中了
,就在下面)


    下面来进一步说明如何使用TLS功能。
    为了方便TLS的使用,MFC设计了CThreadLocal类。它是一个模板类,具体的定义如下

template<class TYPE>
class CThreadLocal : public CThreadLocalObject
{
// Attributes
public:
AFX_INLINE TYPE* GetData()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);
ASSERT(pData != NULL);
return pData;
}
AFX_INLINE TYPE* GetDataNA()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetDataNA();
return pData;
}
AFX_INLINE operator TYPE*()
{ return GetData(); }
AFX_INLINE TYPE* operator->()
{ return GetData(); }

// Implementation
public:
static CNoTrackObject* AFXAPI CreateObject()
{ return new TYPE; }
};
    在使用CThreadLocal时,只要用CThreadLocal<ClassType> name;即可构造一个类型为
ClassType的TLS数据,注意ClassType必须以CNoTrackObject为基类。实际上上述声明定义
了一个名称为name的CThreadLocal对象,但是通过这个CThreadLocal对象,即可生成并访
问类型为ClassType的TLS数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值