?? mfc教程9.htm
字號(hào):
<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>類(lèi)似于線(xiàn)程局部對(duì)象,每一個(gè)進(jìn)程局部變量都有一個(gè)對(duì)應(yīng)的全局CProcessLocal模板對(duì)象。</P>
<P align=justify></P>
<LI><A name=_Toc445889108></A><A name=_Toc445782511></A><A
name=_Toc452640972></A><A name=_Toc457299073></A><B>狀態(tài)對(duì)象的創(chuàng)建</B>
<P></P>
<OL>
<P align=justify>
<LI><B><A name=_Toc457299074></A>狀態(tài)對(duì)象的創(chuàng)建過(guò)程</B>
<P></P></LI></OL></LI></OL></OL></OL>
<P align=justify>回顧前一節(jié)的三個(gè)定義:</P>
<DIR>
<P align=justify>CThreadSlotData* _afxThreadData;</P>
<P align=justify>THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)</P>
<P align=justify>PROCESS_LOCAL(_AFX_BASE_MODULE_STATE,
_afxBaseModuleState)</P></DIR>
<P
align=justify>第一個(gè)僅僅定義了一個(gè)指針;第二和第三個(gè)定義了一個(gè)模板類(lèi)的實(shí)例。相應(yīng)的CThreadSlotData對(duì)象(全局)、_AFX_THREAD_STATE對(duì)象(線(xiàn)程局部)以及_AFX_BASE_MODULE_STATE對(duì)象(進(jìn)程局部)并沒(méi)有創(chuàng)建。當(dāng)然,模塊狀態(tài)對(duì)象的成員模塊-線(xiàn)程對(duì)象也沒(méi)有被創(chuàng)建。這些對(duì)象要到第一次被訪(fǎng)問(wèn)時(shí),才會(huì)被創(chuàng)建,這樣做會(huì)提高加載DLL的速度。</P>
<P align=justify>下面以一個(gè)動(dòng)態(tài)鏈接到MFC DLL的單模塊應(yīng)用程序?yàn)槔f(shuō)明這些對(duì)象的創(chuàng)建過(guò)程。</P>
<P align=justify>當(dāng)?shù)谝淮卧L(fǎng)問(wèn)狀態(tài)信息時(shí),比如使用
AfxGetModuleState得到模塊狀態(tài),導(dǎo)致系列創(chuàng)建過(guò)程的開(kāi)始,如圖9-7所示。</P><IMG height=748 hspace=12
src="MFC教程.files/image149.gif" width=397 align=left>
<P align=justify></P>
<P
align=justify>首先分析語(yǔ)句pState=_afxThreadState。如果_afxThreadData、線(xiàn)程狀態(tài)和模塊狀態(tài)還沒(méi)有創(chuàng)建,該語(yǔ)句可以導(dǎo)致這些數(shù)據(jù)的創(chuàng)建。</P>
<P
align=justify>pState聲明為CNoTrackObject對(duì)象的指針,_afxThreadState聲明為一個(gè)模板CThreadLocal的實(shí)例,pState=_afxThreadData為什么可以通過(guò)編譯器的檢查呢?因?yàn)镃ThreadLocal模板重載了操作符“”*”和“->”,這兩個(gè)運(yùn)算返回CNoTrackObject類(lèi)型的對(duì)象?;仡?.2節(jié)CThreadLocalObject、CThreadLocal的定義,這兩個(gè)操作符運(yùn)算到最后都是調(diào)用CThreadLocalObject的成員函數(shù)GetData。</P>
<P align=justify></P>
<UL>
<P align=justify>
<LI>創(chuàng)建_afxThreadData所指對(duì)象和線(xiàn)程狀態(tài)
<P></P></LI></UL>
<P
align=justify>CThreadLocalObject::GetData用來(lái)獲取線(xiàn)程局部變量(這個(gè)例子中是線(xiàn)程狀態(tài))的值,其參數(shù)用來(lái)創(chuàng)建動(dòng)態(tài)的線(xiàn)程局部變量。圖9-7的上面的虛線(xiàn)框表示其流程:</P>
<P
align=justify>它檢查成員變量m_nSlot是否等于0(線(xiàn)程局部變量是否曾經(jīng)被分配了MFC線(xiàn)程私有空間槽位),檢查全局變量_afxTheadData指針是否為空。如果_afxThreadData空,則創(chuàng)建一個(gè)CThreadSlotData類(lèi)對(duì)象,讓_afxThreadData指向它,這樣本程序的MFC線(xiàn)程局部存儲(chǔ)的管理者被創(chuàng)建。如果m_nSlot等于0,則讓_afxThreadDtata調(diào)用AllocSlot分配一個(gè)槽位并把槽號(hào)保存在m_nSlot中。</P>
<P
align=justify>得到了線(xiàn)程局部變量(線(xiàn)程狀態(tài))所占用的槽位后,委托_afxThreadData調(diào)用GetThreadValue(m_nSlot)得到線(xiàn)程狀態(tài)值(指針)。如果結(jié)果非空,則返回它;如果結(jié)果是NULL,則表明該線(xiàn)程狀態(tài)還沒(méi)有被創(chuàng)建,于是使用參數(shù)創(chuàng)建一個(gè)動(dòng)態(tài)的線(xiàn)程狀態(tài),并使用SetValue把其指針保存在槽m_nSlot中,返回該指針。</P>
<P align=justify></P>
<UL>
<P align=justify>
<LI>創(chuàng)建模塊狀態(tài)
<P></P></LI></UL>
<P
align=justify>得到了線(xiàn)程狀態(tài)的值后,通過(guò)它得到模塊狀態(tài)m_pModuleState。如果m_pModuleState為空,表明該線(xiàn)程狀態(tài)是才創(chuàng)建的,其許多成員變量還沒(méi)有賦值,程序的進(jìn)程模塊狀態(tài)還沒(méi)有被創(chuàng)建。于是調(diào)用函數(shù)_afxBaseModule.GetData,導(dǎo)致進(jìn)程模塊狀態(tài)被創(chuàng)建。</P>
<P align=justify>圖9-7的下面一個(gè)虛線(xiàn)框表示了CProcessLocalObject::GetData的創(chuàng)建過(guò)程:</P>
<P
align=justify>_afxBaseModule首先檢查成員變量m_pObject是否空,如果非空就返回它,即進(jìn)程模塊狀態(tài)指針;否則,在堆中創(chuàng)建一個(gè)動(dòng)態(tài)的_AFX_BASE_MODULE_STATE對(duì)象,返回。</P>
<P
align=justify>從上述兩個(gè)GetData的實(shí)現(xiàn)可以看出,CThreadLocal模板對(duì)象負(fù)責(zé)線(xiàn)程局部變量的創(chuàng)建和管理(查詢(xún),修改,刪除);CProcessLocal模板對(duì)象負(fù)責(zé)進(jìn)程局部變量的創(chuàng)建和管理(查詢(xún),修改,刪除)。</P>
<UL>
<P align=justify>
<LI>模塊-線(xiàn)程狀態(tài)的創(chuàng)建
<P></P></LI></UL>
<P
align=justify>模塊狀態(tài)的成員模塊-線(xiàn)程狀態(tài)m_thread的創(chuàng)建類(lèi)似于線(xiàn)程狀態(tài)的創(chuàng)建:當(dāng)?shù)谝淮卧L(fǎng)問(wèn)m_thread所對(duì)應(yīng)的CThreadLocal模板對(duì)象時(shí),給m_thread分配MFC線(xiàn)程局部存儲(chǔ)的私有槽號(hào)m_nSlot,并動(dòng)態(tài)地創(chuàng)建_AFX_MODULE_THREAD_STATE對(duì)象,保存對(duì)象指針在m_nSlot槽中。</P>
<OL>
<OL>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc457299075></A><B>創(chuàng)建過(guò)程所涉及的幾個(gè)重要函數(shù)的算法</B>
<P></P></LI></OL></OL></OL></OL>
<P align=justify>創(chuàng)建過(guò)程所涉及的幾個(gè)重要函數(shù)的算法描述如下:</P>
<OL>
<P align=justify>
<LI>AllocSlot
<P></P>
<P
align=justify>AllocSlot用來(lái)分配線(xiàn)程的MFC私有存儲(chǔ)空間的槽號(hào)。由于該函數(shù)要修改全局變量_afxThreadData,所以必須使用m_sect關(guān)鍵段對(duì)象來(lái)同步多個(gè)線(xiàn)程對(duì)該函數(shù)的調(diào)用。</P>
<P align=justify>CThreadSlotData::AllocSlot()</P>
<P align=justify>{</P>
<P align=justify>進(jìn)入關(guān)鍵段代碼(EnterCriticalSection(m_sect);)</P>
<P align=justify>搜索m_pSlotData,查找空槽(SLOT)</P>
<P align=justify>如果不存在空槽(第一次進(jìn)入時(shí),肯定不存在)</P>
<P align=justify>分配或再分配內(nèi)存以創(chuàng)建新槽,</P>
<P align=justify>指針m_pSlotData指向分配的地址。</P>
<P align=justify>得到新槽(SLOT)</P>
<P align=justify>標(biāo)志該SLOT為已用</P>
<P align=justify>記錄最新可用的SLOT到成員變量m_nRover中。</P>
<P align=justify>離開(kāi)關(guān)鍵段代碼(LeaveCriticalSection(m_sect);)</P>
<P align=justify>返回槽號(hào)</P>
<P align=justify>}</P>
<P align=justify></P>
<LI>GetThreadValue
<P></P>
<P
align=justify>GetThreadValue用來(lái)獲取調(diào)用線(xiàn)程的第slot個(gè)線(xiàn)程局部變量的值。每一個(gè)線(xiàn)程局部變量都占用一個(gè)且只一個(gè)槽位。</P>
<P align=justify>CThreadSlotData::GetThreadValue(int slot)</P>
<P align=justify>{</P>
<P align=justify>//得到一個(gè)CThreadData型的指針pData</P>
<P align=justify>//pData指向MFC線(xiàn)程私有存儲(chǔ)空間。</P>
<P align=justify>//m_tlsIndex在_afxThreadData創(chuàng)建時(shí)由構(gòu)造函數(shù)創(chuàng)建</P>
<P align=justify>pData=(CThreadData*)TlsGetValue(m_tlsIndex),。</P>
<P align=justify>如果指針空或slot>pData->nCount, 則返回空。</P>
<P align=justify>否則,返回pData</P>
<P align=justify>}</P>
<P align=justify></P>
<LI>SetValue
<P></P></LI></OL>
<P align=justify>SetValue用來(lái)把調(diào)用線(xiàn)程的第slot個(gè)線(xiàn)程局部變量的值(指針)存放到線(xiàn)程的MFC私有存儲(chǔ)空間的第slot個(gè)槽位。</P>
<P align=justify>CThreadSlotData::SetValue(int slot, void *pValue)</P>
<P align=justify>{</P>
<P align=justify>//通過(guò)TLS索引得到線(xiàn)程的MFC私有存儲(chǔ)空間</P>
<DIR>
<P align=justify>pData = (CThreadData*)TlsGetValue(m_tlsIndex)</P>
<P align=justify></P>
<P align=justify>//沒(méi)有得到值或者pValue非空且當(dāng)前槽號(hào),即</P>
<P align=justify>//線(xiàn)程局部變量的個(gè)數(shù)</P>
<P align=justify>//大于使用當(dāng)前局部變量的線(xiàn)程個(gè)數(shù)時(shí)</P>
<P align=justify>if (pData NULL or slot > pData->nCount &&
pValue!=NULL)</P>
<P align=justify>{</P>
<DIR>
<P align=justify>if pData NULL //當(dāng)前線(xiàn)程第一次訪(fǎng)問(wèn)該線(xiàn)程局部變量</P>
<P align=justify>{</P>
<DIR>
<DIR>
<P align=justify>創(chuàng)建一個(gè)CThreadData實(shí)例;</P>
<P align=justify>添加到CThreadSlotData::m_list;</P>
<P align=justify>令pData指向它;</P></DIR></DIR>
<P align=justify>}</P>
<P align=justify>按目前為止,線(xiàn)程局部變量的個(gè)數(shù)為pData->pData分配或重分配內(nèi)存,</P>
<P align=justify>用來(lái)容納指向真正線(xiàn)程數(shù)據(jù)的指針</P>
<P align=justify>調(diào)用TlsSetValue(pData)保存pData</P></DIR></DIR>
<P align=justify>}</P>
<P align=justify></P>
<DIR>
<P align=justify>//把指向真正線(xiàn)程數(shù)據(jù)的pValue保存在pData對(duì)應(yīng)的slot中</P>
<P align=justify>pData->pData[slot] = pValue</P></DIR>
<P align=justify>}</P>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889109></A><A name=_Toc445782512></A><A
name=_Toc452640973></A><A name=_Toc457299076></A><B>管理狀態(tài)</B>
<P></P>
<P align=justify>在描述了MFC狀態(tài)的實(shí)現(xiàn)機(jī)制之后,現(xiàn)在來(lái)討論MFC的狀態(tài)管理和相關(guān)狀態(tài)的作用。</P>
<OL>
<P align=justify>
<LI><A name=_Toc445889110></A><A name=_Toc445782513></A><A
name=_Toc452640974></A><A name=_Toc457299077></A><B>模塊狀態(tài)切換</B>
<P></P></LI></OL></LI></OL></OL>
<P align=justify>模塊狀態(tài)切換就是把當(dāng)前線(xiàn)程的線(xiàn)程狀態(tài)的m_pModuleState指針指向即將運(yùn)行模塊的模塊狀態(tài)。</P>
<P
align=justify>MFC使用AFX_MANAGE_STATE宏來(lái)完成模塊狀態(tài)的切換,即進(jìn)入模塊時(shí)使用當(dāng)前模板的模板狀態(tài),并保存原模板狀態(tài);退出模塊時(shí)恢復(fù)原來(lái)的模塊狀態(tài)。這相當(dāng)于狀態(tài)的壓棧和出棧。實(shí)現(xiàn)原理如下。</P>
<P align=justify>先看MFC關(guān)于AFX_MANAGE_STATE的定義:</P>
<P align=justify>#ifdef _AFXDLL</P>
<DIR>
<P align=justify>struct AFX_MAINTAIN_STATE</P>
<P align=justify>{</P>
<P align=justify>AFX_MAINTAIN_STATE(AFX_MODULE_STATE* pModuleState);</P>
<P align=justify>~AFX_MAINTAIN_STATE();</P>
<P align=justify>protected:</P>
<P align=justify>AFX_MODULE_STATE* m_pPrevModuleState;</P>
<P align=justify>};</P>
<P align=justify>//AFX_MANAGE_STATE宏的定義:</P>
<P align=justify>#define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE
_ctlState(p);</P></DIR>
<P align=justify>#else // _AFXDLL</P>
<DIR>
<P align=justify>#define AFX_MANAGE_STATE(p)</P></DIR>
<P align=justify>#endif //!_AFXDLL</P>
<P align=justify>如果使用MFC
DLL,MFC提供類(lèi)AFX_MAINTAIN_STATE來(lái)實(shí)現(xiàn)狀態(tài)的壓棧和出棧,AFX_MANAGE_SATATE宏的作用是定義一個(gè)AFX_MAINTAIN_STATE類(lèi)型的局部變量_ctlState。</P>
<P
align=justify>AFX_MAINTAIN_STATE的構(gòu)造函數(shù)在其成員變量m_pPrevModuleState中保存當(dāng)前的模塊狀態(tài)對(duì)象,并把參數(shù)指定的模塊狀態(tài)設(shè)定為當(dāng)前模塊狀態(tài)。所以該宏作為入口點(diǎn)的第一條語(yǔ)句就切換了模塊狀態(tài)。</P>
<P
align=justify>在退出模塊時(shí),局部變量_ctlState將自動(dòng)地銷(xiāo)毀,這導(dǎo)致AFX_MAINTAIN_STATE的析構(gòu)函數(shù)被調(diào)用,析構(gòu)函數(shù)把保存在m_pPrevModuleState的狀態(tài)設(shè)置為當(dāng)前狀態(tài)。</P>
<P align=justify></P>
<P align=justify>AFX_MANAGE_SATATE的參數(shù)在不同場(chǎng)合是不一樣的,例如,</P>
<UL>
<P align=justify>
<LI>DLL的輸出函數(shù)使用
<P></P></LI></UL>
<P align=justify>AFX_MANAGE_SATATE(AfxGetStaticModuleState());</P>
<P align=justify>其中,AfxGetStaticModuleState返回DLL的模塊狀態(tài)afxModuleState。</P>
<UL>
<P align=justify>
<LI>窗口函數(shù)使用
<P></P></LI></UL>
<P align=justify>AFX_MANAGE_STATE(_afxBaseModuleState.GetData());</P>
<P align=justify>其中,_afxBaseModuleState.GetData()返回的是應(yīng)用程序的全局模塊狀態(tài)。</P>
<P align=justify>OLE使用的模塊切換方法有所不同,這里不作討論。</P>
<P align=justify></P>
<P align=justify>上面討論了線(xiàn)程執(zhí)行行不同模塊的代碼時(shí)切換模塊狀態(tài)的情況。在線(xiàn)程創(chuàng)建時(shí)怎么處理模塊狀態(tài)呢?</P>
<UL>
<P align=justify>
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -