⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mfc

📁 很好的MFC书籍
💻
📖 第 1 页 / 共 5 页
字号:
        <P align=justify>~CThreadSlotData();</P>
        <P align=justify>};</P>
        <P 
        align=justify>通过TLS索引m_tlsIndex,CThreadSlotData对象(_afxThreadData)为每一个线程分配一个线程私有的存储空间并管理该空间。它把这个空间划分为若干个槽,每个槽放一个线程私有的数据指针,这样每个线程就可以存放任意个线程私有的数据指针。</P>
        <P align=justify></P>
        <LI><A name=_Toc457299069></A><B>CThreadSlotData的一些数据成员</B> 
        <P></P>
        <P align=justify>在CThreadSlotData类的定义中所涉及的类或者结构定义如下:</P>
        <P align=justify>(1)m_sect</P>
        <P 
        align=justify>m_sect是一个关键段变量,在_afxThreadData创建时初始化。因为_afxThreadData是一个全局变量,所以必须通过m_sect来同步多个线程对该变量的并发访问。</P>
        <P align=justify>(2)m_nAlloc和m_pSlotData</P>
        <P 
        align=justify>m_nAlloc表示已经分配槽的数目,它代表了线程局部变量的个数。每一个线程局部变量都对应一个槽,每个槽对应一个线程局部变量。槽使用CSlotData类来管理。</P>
        <P align=justify>CSlotData的定义如下:</P>
        <P align=justify>struct CSlotData{</P>
        <P align=justify>DWORD dwFlags; // slot flags (allocated/not 
        allocated)</P>
        <P align=justify>HINSTANCE hInst; // module which owns this slot</P>
        <P align=justify>};</P>
        <P align=justify>该结构用来描述槽的使用:</P>
        <P align=justify>域dwFlags表示槽的状态,即被占用或者没有;</P>
        <P align=justify>域hInst表示使用该槽的模块的句柄。</P>
        <P 
        align=justify>m_pSlotData表示一个CSlotData类型的数组,用来描述各个槽。该数组通过成员函数AllocSlot和FreeSlot来动态地管理,见图9-6。</P>
        <P align=justify>(3)m_list</P>
        <P align=justify>先讨论CThreadData 类。CThreadData定义如下:</P>
        <P align=justify>struct CThreadData : public CNoTrackObject{</P>
        <P align=justify>CThreadData* pNext; // required to be member of 
        CSimpleList</P>
        <P align=justify>int nCount; // current size of pData</P>
        <P align=justify>LPVOID* pData; // actual thread local data (indexed by 
        nSlot)</P>
        <P align=justify>};</P>
        <P align=justify>该结构用来描述CThreadSlotData为每个线程管理的线程局部空间:</P>
        <P align=justify>域pNext把各个线程的CThreadData项目链接成一个表,即把各个线程的线程私有空间链接起来;</P>
        <P align=justify>域nCount表示域pData的尺寸,即存储了多少个线程私有数据;</P>
        <P 
        align=justify>pData表示一个LPVOID类型的数组,数组中的每一个元素保存一个指针,即线程私有数据指针,该指针指向一个在堆中分配的真正存储线程私有数据的地址。数组元素的个数和槽的个数相同,每个线程局部变量(THREAD_LOCAL定义的变量)都有一个对应的槽号,用该槽号作为下标来引用pData。</P>
        <P 
        align=justify>m_list表示一个CThreadData类型的指针数组,数组中的各项指向各个线程的线程私有空间,每个线程在数组中都有一个对应项。该数组通过GetValue、SetValue、DeleteValues等成员函数来管理,见图9-6。</P>
        <P align=justify></P>
        <LI><A name=_Toc457299070></A><B>_afxThreadData</B> 
        <P></P></LI></OL></LI></OL></LI></OL></LI></OL><B><IMG height=135 hspace=12 
src="MFC教程9_MFC的状态.files/image147.gif" width=397 align=left vspace=18> </B>
<P 
align=justify>_afxThreadData仅仅定义为一个CThreadSlotData类型的指针,所指对象在第一次被引用时创建,在此之前该指针为空。下文_afxThreadData含义是它所指的对象。图9-5、9-6图解了MFC的线程局部存储机制的实现。</P>
<P 
align=justify>图9-5表示_afxTheadData使用TLS技术负责给进程分配一个TLS索引,然后使用TLS索引为进程的每一个线程分配线程局部存储空间。</P>
<P 
align=justify>图9-6表示每个线程的的局部存储空间可以分多个槽,每个槽可以放一个线程私有的数据指针。_afxThreadData负责给线程局部变量分配槽号并根据槽号存取数据。图的左半部分描述了管理槽的m_pSlotData及类CSlotData的结构,右半部分描述了管理MFC线程私有空间的m_list及类CThreadData的结构。</P>
<P align=justify>结合图9-6,对MFC线程局部存储机制总结如下:</P>
<UL>
  <P align=justify>
  <LI>每个线程局部变量(宏THREAD_LOCAL定义)占用一个槽,并有一个槽号。。 
  <P></P>
  <P align=justify></P>
  <LI>每个线程都有自己的MFC局部存储空间(下文多次使用“线程的MFC局部存储空间”,表示和此处相同的概念)。 
  <P></P>
  <P align=justify></P>
  <LI>通过TLS索引得到的是一个指针P1,它指向线程的MFC局部存储空间。 
  <P></P>
  <P align=justify></P>
  <LI>通过指针P1和线程局部变量在空间所占用的槽号,得到该槽所存储的线程私有的数据指针,即真正的线程私有数据的地址P2; 
  <P></P>
  <P align=justify></P>
  <LI>从地址P2得到数据D。 
  <P></P></LI></UL>
<P 
align=justify>这个过程相当于几重间接寻址:先得到TLS线程私有数据指针,从TLS线程私有数据指针得到线程的MFC线程局部存储空间,再从MFC局部存储空间的对应槽得到一个线程私有的数据指针,从该指针得到最终的线程私有数据。如果没有这种机制,使用Win32 
TLS只要一次间接寻址:得到TLS线程私有数据指针,从该指针得到最终的线程私有数据。</P><IMG height=271 hspace=12 
src="MFC教程9_MFC的状态.files/image148.gif" width=433 align=left vspace=18> 
<OL>
  <OL>
    <OL>
      <P align=justify>
      <LI><A name=_Toc452640970></A><A 
      name=_Toc457299071></A><B>线程状态_afxThreadState</B> 
      <P></P>
      <P 
      align=justify>从上一节知道了MFC的线程局部存储机制。但有一点还不清楚,即某个线程局部变量所占用的槽号是怎么保存的呢?关于这点可从线程局部的线程状态变量_afxThreadState的实现来分析MFC的作法。变量_afxThreadState的定义如下:</P>
      <P align=justify>THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)</P>
      <P align=justify></P>
      <P align=justify>THREAD_LOCAL 是一个宏,THREAD_LOCAL(class_name, 
      ident_name)宏展开后如下:</P>
      <P align=justify>AFX_DATADEF CThreadLocal&lt;class_name&gt; 
ident_name;</P>
      <P align=justify>这里,CThreadLocal是一个类模板,从CThreadLocalObject类继承。</P>
      <P align=justify>CThreadLocalObject和CThreadLocal的定义如下:</P>
      <P align=justify>class CThreadLocalObject</P>
      <P align=justify>{</P>
      <P align=justify>public:</P>
      <P align=justify>// Attributes</P>
      <P align=justify>CNoTrackObject* GetData(CNoTrackObject* (AFXAPI* </P>
      <P align=justify>pfnCreateObject)());</P>
      <P align=justify>CNoTrackObject* GetDataNA();</P>
      <P align=justify></P>
      <P align=justify>// Implementation</P>
      <P align=justify>int m_nSlot;</P>
      <P align=justify>~CThreadLocalObject();</P>
      <P align=justify>};</P>
      <P 
      align=justify>CThreadLocalObject用来帮助实现一个线程局部的变量。成员变量m_nSlot表示线程局部变量在MFC线程局部存储空间中占据的槽号。GetDataNA用来返回变量的值。GetData也可以返回变量的值,但是如果发现还没有给该变量分配槽号(m_slot=0),则给它分配槽号并在线程的MFC局部空间为之分配一个槽;如果在槽m_nSlot还没有数据(为空),则调用参数pfnCreateObject传递的函数创建一个数据项,并保存到槽m_nSlot中。</P>
      <P align=justify></P>
      <P align=justify>template&lt;class TYPE&gt;</P>
      <P align=justify>class CThreadLocal : public CThreadLocalObject</P>
      <P align=justify>{</P>
      <P align=justify>// Attributes</P>
      <P align=justify>public:</P>
      <P align=justify>inline TYPE* GetData()</P>
      <P align=justify>{</P>
      <P align=justify>TYPE* pData = 
      (TYPE*)CThreadLocalObject::GetData(&amp;CreateObject);</P>
      <P align=justify>ASSERT(pData != NULL);</P>
      <P align=justify>return pData;</P>
      <P align=justify>}</P>
      <P align=justify>inline TYPE* GetDataNA()</P>
      <P align=justify>{</P>
      <P align=justify>TYPE* pData = (TYPE*)CThreadLocalObject::GetDataNA();</P>
      <P align=justify>return pData;</P>
      <P align=justify>}</P>
      <P align=justify>inline operator TYPE*()</P>
      <P align=justify>{ return GetData(); }</P>
      <P align=justify>inline TYPE* operator-&gt;()</P>
      <P align=justify>{ return GetData(); }</P>
      <P align=justify></P>
      <P align=justify>// Implementation</P>
      <P align=justify>public:</P>
      <P align=justify>static CNoTrackObject* AFXAPI CreateObject()</P>
      <P align=justify>{ return new TYPE; }</P>
      <P align=justify>};</P>
      <P 
      align=justify>CThreadLocal模板用来声明任意类型的线程私有的变量,因为通过模板可以自动的正确的转化(cast)指针类型。程序员可以使用它来实现自己的线程局部变量,正如MFC实现线程局部的线程状态变量和模块-线程变量一样。</P>
      <P 
      align=justify>CThrealLocal的成员函数CreateObject用来创建动态的指定类型的对象。成员函数GetData调用了基类CThreadLocalObject的同名函数,并且把CreateObject函数的地址作为参数传递给它。</P>
      <P 
      align=justify>另外,CThreadLocal模板重载了操作符号“*”、“-&gt;”,这样编译器将自动地进行有关类型转换,例如:</P>
      <P align=justify>_AFX_THREAD_STATE *pStata = _afxThreadState</P>
      <P align=justify>是可以被编译器接收的。</P>
      <P align=justify></P>
      <P align=justify>现在回头来看_afxThreadState的定义:</P>
      <P align=justify>从以上分析可以知道,THREAD_LOCAL(class_name, 
      ident_name)定义的结果并没有产生一个名为ident_name的class_name类的实例,而是产生一个CThreadLocal模板类(确切地说,是其派生类)的实例,m_nSlot初始化为0。所以,_afxThreadState实质上是一个CThreadLocal模板类的全局变量。每一个线程局部变量都对应了一个全局的CThreadLoacl模板类对象,模板对象的m_nSlot记录了线程局部变量对象的槽号。</P>
      <P align=justify></P>
      <LI><A name=_Toc445889107></A><A name=_Toc445782510></A><A 
      name=_Toc452640971></A><A 
      name=_Toc457299072></A><B>进程模块状态afxBaseModuleState</B> 
      <P></P>
      <P align=justify>进程模块状态定义如下:</P>
      <P align=justify>PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, 
      _afxBaseModuleState)</P>
      <P align=justify>表示它是一个_AFX_BASE_MODULE_STATE类型的进程局部(process 
local)的变量。</P>
      <P 
      align=justify>进程局部变量的实现方法主要是为了用于Win32s下。在Win32s下,一个DLL模块如果被多个应用程序调用,它将让这些程序共享它的全局数据。为了DLL的全局数据一个进程有一份独立的拷贝,MFC设计了进程私有的实现方法,实际上就是在进程的堆(Heap)中分配全局数据的内存空间。</P>
      <P 
      align=justify>在Win32下,DLL模块的数据和代码被映射到调用进程的虚拟空间,也就是说,DLL定义的全局变量是进程私有的;所以进程局部变量的实现并不为Win32所关心。但是,不是说afxBaseModuleState不重要,仅仅是采用PROCESS_LOCAL技术声明它是进程局部变量不是很必要了。PROCESS_LOCAL(class_name, 
      ident_name)宏展开后如下:</P>
      <P align=justify>AFX_DATADEF CProcessLocal&lt;class_name&gt; 
      ident_name;</P>
      <P align=justify>这里,CProcessLocal是一个类模板,从CProcessLocalObject类继承。</P>
      <P align=justify>CProcessLocalObject和CProcessLocal的定义如下:</P>
      <P align=justify>class CProcessLocalObject</P>
      <P align=justify>{</P>
      <P align=justify>public:</P>
      <P align=justify>// Attributes</P>
      <P align=justify>CNoTrackObject* GetData(CNoTrackObject* (AFXAPI* </P>
      <P align=justify>pfnCreateObject)());</P>
      <P align=justify></P>
      <P align=justify>// Implementation</P>
      <P align=justify>CNoTrackObject* volatile m_pObject;</P>
      <P align=justify>~CProcessLocalObject();</P>
      <P align=justify>};</P>
      <P align=justify></P>
      <P align=justify>template&lt;class TYPE&gt;</P>
      <P align=justify>class CProcessLocal : public CProcessLocalObject</P>
      <P align=justify>{</P>
      <P align=justify>// Attributes</P>
      <P align=justify>public:</P>
      <P align=justify>inline TYPE* GetData()</P>
      <P align=justify>{</P>
      <P align=justify>TYPE* pData 
      =(TYPE*)CProcessLocalObject::GetData(&amp;CreateObject);</P>
      <P align=justify>ASSERT(pData != NULL);</P>
      <P align=justify>return pData;</P>
      <P align=justify>}</P>
      <P align=justify>inline TYPE* GetDataNA()</P>
      <P align=justify>{ return (TYPE*)m_pObject; }</P>
      <P align=justify>inline operator TYPE*()</P>
      <P align=justify>{ return GetData(); }</P>
      <P align=justify>inline TYPE* operator-&gt;()</P>
      <P align=justify>{ return GetData(); }</P>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -