?? mfc教程9.htm
字號:
<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教程.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教程.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<class_name>
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<class TYPE></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(&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->()</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模板重載了操作符號“*”、“->”,這樣編譯器將自動地進行有關類型轉換,例如:</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<class_name>
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<class TYPE></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(&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->()</P>
<P align=justify>{ return GetData(); }</P>
<P align=justify></P>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -