分类:
2010-12-08 18:08:14
6 状态对象的创建
(1) 状态对象的创建过程
回顾前一节的三个定义:
CThreadSlotData* _afxThreadData;
THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)
第一个仅仅定义了一个指针;第二和第三个定义了一个模板类的实例。相应的CThreadSlotData对象(全局)、_AFX_THREAD_STATE对象(线程局部)以及_AFX_BASE_MODULE_STATE对象(进程局部)并没有创建。当然,模块状态对象的成员模块-线程对象也没有被创建。这些对象要到第一次被访问时,才会被创建,这样做会提高加载DLL的速度。
(1) 创建线程状态
下面以一个动态链接到MFC DLL的单模块应用程序为例,说明这些对象的创建过程。
当第一次访问状态信息时,比如使用 AfxGetModuleState得到模块状态,导致系列创建过程的开始,如图9-7所示。
。
首先分析语句pState=_afxThreadState。如果_afxThreadData、线程状态和模块状态还没有创建,该语句可以导致这些数据的创建。
pState声明为CNoTrackObject对象的指针,_afxThreadState声明为一个模板CThreadLocal的实例,pState=_afxThreadData为什么可以通过编译器的检查呢?因为CThreadLocal模板重载了操作符“”*”和“->”,这两个运算返回CNoTrackObject类型的对象。回顾3.2节CThreadLocalObject、CThreadLocal的定义,这两个操作符运算到最后都是调用CThreadLocalObject的成员函数GetData。
·
创建_afxThreadData所指对象和线程状态
CThreadLocalObject::GetData用来获取线程局部变量(这个例子中是线程状态)的值,其参数用来创建动态的线程局部变量。图9-7的上面的虚线框表示其流程:
它检查成员变量m_nSlot是否等于0(线程局部变量是否曾经被分配了MFC线程私有空间槽位),检查全局变量_afxTheadData指针是否为空。如果_afxThreadData空,则创建一个CThreadSlotData类对象,让_afxThreadData指向它,这样本程序的MFC线程局部存储的管理者被创建。如果m_nSlot等于0,则让_afxThreadDtata调用AllocSlot分配一个槽位并把槽号保存在m_nSlot中。
得到了线程局部变量(线程状态)所占用的槽位后,委托_afxThreadData调用GetThreadValue(m_nSlot)得到线程状态值(指针)。如果结果非空,则返回它;如果结果是NULL,则表明该线程状态还没有被创建,于是使用参数创建一个动态的线程状态,并使用SetValue把其指针保存在槽m_nSlot中,返回该指针。
(2) 创建模块状态
得到了线程状态的值后,通过它得到模块状态m_pModuleState。如果m_pModuleState为空,表明该线程状态是才创建的,其许多成员变量还没有赋值,程序的进程模块状态还没有被创建。于是调用函数_afxBaseModule.GetData,导致进程模块状态被创建。
图9-7的下面一个虚线框表示了CProcessLocalObject::GetData的创建过程:
_afxBaseModule首先检查成员变量m_pObject是否空,如果非空就返回它,即进程模块状态指针;否则,在堆中创建一个动态的_AFX_BASE_MODULE_STATE对象,返回。
从上述两个GetData的实现可以看出,CThreadLocal模板对象负责线程局部变量的创建和管理(查询,修改,删除);CProcessLocal模板对象负责进程局部变量的创建和管理(查询,修改,删除)。
(3) 创建模块-线程状态
模块状态的成员模块-线程状态m_thread的创建类似于线程状态的创建:当第一次访问m_thread所对应的CThreadLocal模板对象时,给m_thread分配MFC线程局部存储的私有槽号m_nSlot,并动态地创建_AFX_MODULE_THREAD_STATE对象,保存对象指针在m_nSlot槽中。