第九章 MFC的狀態_第1頁
第九章 MFC的狀態_第2頁
第九章 MFC的狀態_第3頁
第九章 MFC的狀態_第4頁
第九章 MFC的狀態_第5頁
已閱讀5頁,還剩12頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、第九章 MFC的狀態    MFC的狀態 MFC定義了多種狀態信息,這里要介紹的是模塊狀態、進程狀態、線程狀態。這些狀態可以組合在一起,例如MFC句柄映射就是模塊和線程局部有效的,屬于模塊-線程狀態的一部分。模塊狀態 這里模塊的含義是:一個可執行的程序或者一個使用MFC DLL的DLL,比如一個OLE控件就是一個模塊。一個應用程序的每一個模塊都有一個狀態,模塊狀態包括這樣一些信息:用來加載資源的 Windows實例句柄、指向當前CWinApp或者CWinThread對象的指針、OLE模塊的引用計數、Windows對象與相應的MFC對象

2、之間的映射。只有單一模塊的應用程序的狀態如圖9-1所示。m_pModuleState 指針是線程對象的成員變量,指向當前模塊狀態信息(一個AFX_MODULE_STATE結構變量)。當程序運行進入某個特定的模塊時,必須保證當前使用的模塊狀態是有效的模塊狀態是這個特定模塊的模塊狀態。所以,每個線程對象都有一個指針指向有效的模塊狀態,每當進入某個模塊時都要使它指向有效模塊狀態,這對維護應用程序全局狀態和每個模塊狀態的完整性來說是非常重要的。為了作到這一點,每個模塊的所有入口點有責任實現模塊狀態的切換。模塊的入口點包括:DLL的輸出函數;OLE/COM界面的成員函數;窗口過程。在講述窗口過程和動態鏈

3、接到MFC DLL的規則DLL時,曾提到了語句AFX_MANAGE_STATE(AfxGetStaticModuleState( ),它就是用來在入口點切換模塊狀態的。其實現機制將在后面9.4.1節講解。多個模塊狀態之間切換的示意圖如圖9-2所示。圖9-2中,m_pModuleState總是指向當前模塊的狀態。模塊、進程和線程狀態的數據結構 MFC定義了一系列類或者結構,通過它們來實現狀態信息的管理。這一節將描述它們的關系,并逐一解釋它們的數據結構、成員函數等。層次關系 圖9-3顯示了線程狀態、模塊狀態、線程-模塊狀態等幾個類的層次關系:線程狀態用類_AFX_THREAD_

4、STATE描述,模塊狀態用類AFX_MODULE_STATE描述,模塊-線程狀態用類AFX_MODULE_THREAD_STATE描述。這些類從類CNoTrackObject派生。進程狀態類用_AFX_BASE_MODULE_STATE描述,從模塊狀態類AFX_MODULE_STATE派生。進程狀態是了一個可以獨立執行的MFC應用程序的模塊狀態。還有其他狀態如DLL的模塊狀態等也從模塊狀態類_AFX_MODULE_STATE派生。圖9-4顯示了這幾個類的交互關系。從圖9-4可以看出:首先,每個線程有一個線程狀態,線程狀態的指針m_pModuleState和m_pPreModuleState分別

5、指向線程當前運行模塊的狀態或前一運行模塊的狀態;其次,每一個模塊狀態都有一個線程局部的變量用來存儲模塊-線程狀態。下面各小節列出狀態信息管理所涉及的各個類的定義。CNoTrackObject類 在圖9-3中, CnoTrackObject是根類,所有狀態類都是從它這里派生的,其定義如下:class CNoTrackObjectpublic:void* PASCAL operator new(size_t nSize);void PASCAL operator delete(void*);#if defined(_DEBUG) && !defined(_AFX_NO_D

6、EBUG_CRT)void* PASCAL operator new(size_t nSize, LPCSTR, int);#endifvirtual CNoTrackObject() ;該類的析構函數是虛擬函數;而且,CNoTrackObject重載new操作符用來分配內存,重載delete操作符號用來釋放內存,內部通過LocalAlloc/LocalFree提供了一個低層內存分配器(Low_level alloctor)。AFX_MODULE_STATE類 AFX_MODULE_STATE類的定義如下:/ AFX_MODULE_STATE (global data for a m

7、odule)class AFX_MODULE_STATE : public CNoTrackObjectpublic:#ifdef _AFXDLLAFX_MODULE_STATE(BOOL bDLL,WNDPROC pfnAfxWndProc,DWORD dwVersion);AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc,DWORD dwVersion,BOOL bSystem);#elseAFX_MODULE_STATE(BOOL bDLL);#endifAFX_MODULE_STATE();CWinApp* m_pCurrentWinA

8、pp;HINSTANCE m_hCurrentInstanceHandle;HINSTANCE m_hCurrentResourceHandle;LPCTSTR m_lpszCurrentAppName;BYTE m_bDLL;/ TRUE if module is a DLL, FALSE if it is an EXE/TRUE if module is a "system" module, FALSE if notBYTE m_bSystem;BYTE m_bReserved2; / padding/Runtime class data:#ifdef _AFXDLLC

9、RuntimeClass* m_pClassInit;#endifCTypedSimpleList<CRuntimeClass*> m_classList;/ OLE object factories#ifndef _AFX_NO_OLE_SUPPORT#ifdef _AFXDLLCOleObjectFactory* m_pFactoryInit;#endifCTypedSimpleList<COleObjectFactory*> m_factoryList;#endif/ number of locked OLE objectslong m_nObjectCount;

10、BOOL m_bUserCtrl;/ AfxRegisterClass and AfxRegisterWndClass dataTCHAR m_szUnregisterList4096;#ifdef _AFXDLLWNDPROC m_pfnAfxWndProc;DWORD m_dwVersion; / version that module linked against#endif/ variables related to a given process in a module/ (used to be AFX_MODULE_PROCESS_STATE)#ifdef _AFX_OLD_EXC

11、EPTIONS/ exceptionsAFX_TERM_PROC m_pfnTerminate;#endifvoid (PASCAL *m_pfnFilterToolTipMessage)(MSG*, CWnd*);#ifdef _AFXDLL/ CDynLinkLibrary objects (for resource chain)CTypedSimpleList<CDynLinkLibrary*> m_libraryList;/ special case for MFCxxLOC.DLL (localized MFC resources)HINSTANCE m_appLangD

12、LL;#endif#ifndef _AFX_NO_OCC_SUPPORT/ OLE control container managerCOccManager* m_pOccManager;/ locked OLE controlsCTypedSimpleList<COleControlLock*> m_lockList;#endif#ifndef _AFX_NO_DAO_SUPPORT_AFX_DAO_STATE* m_pDaoState;#endif#ifndef _AFX_NO_OLE_SUPPORT/ Type library cachesCTypeLibCache m_ty

13、peLibCache;CMapPtrToPtr* m_pTypeLibCacheMap;#endif/ define thread local portions of module stateTHREAD_LOCAL(AFX_MODULE_THREAD_STATE, m_thread);從上面的定義可以看出,模塊狀態信息分為如下幾類:模塊信息,資源信息,對動態鏈接到MFC DLL的支持信息,對擴展DLL的支持信息,對DAO的支持信息,對OLE的支持信息,模塊-線程狀態信息。模塊信息包括實例句柄、資源句柄、應用程序名稱、指向應用程序的指針、是否為DLL模塊、模塊注冊的窗口類,等等。其中,成員變量

14、m_fRegisteredClasses、m_szUnregisterList曾經在討論MFC的窗口注冊時提到過它們的用處。在“#ifdef _AFXDLL#endif”條件編譯范圍內的是支持MFC DLL的數據;在“#ifndef _AFX_NO_OLE_SUPPOR#endif”條件編譯范圍內的是支持OLE的數據;在“#ifndef _AFX_NO_OCC_SUPPOR#endif”條件編譯范圍內的是支持OLE控件的數據;在“#ifndef _AFX_NO_DAO_SUPPORT”條件編譯范圍內的是支持DAO的數據。THREAD_LOCAL宏定義了線程私有的模塊-線程類型的變量m_thre

15、ad。_AFX_BASE_MODULE_STATE 該類定義如下:class _AFX_BASE_MODULE_STATE : public AFX_MODULE_STATEpublic:#ifdef _AFXDLL_AFX_BASE_MODULE_STATE() : AFX_MODULE_STATE(TRUE,AfxWndProcBase, _MFC_VER)#else_AFX_BASE_MODULE_STATE() : AFX_MODULE_STATE(TRUE)#endif ;由定義可見,該類沒有在_AFX_MODULE_STATE類的基礎上增加數據。它類用來實現一個MFC應用

16、程序模塊的狀態信息。_AFX_THREAD_STATE 該類定義如下:class _AFX_THREAD_STATE : public CNoTrackObjectpublic:_AFX_THREAD_STATE();virtual _AFX_THREAD_STATE();/ override for m_pModuleState in _AFX_APP_STATEAFX_MODULE_STATE* m_pModuleState;AFX_MODULE_STATE* m_pPrevModuleState;/ memory safety pool for temp mapsvoid* m

17、_pSafetyPoolBuffer; / current buffer/ thread local exception contextAFX_EXCEPTION_CONTEXT m_exceptionContext;/ CWnd create, gray dialog hook, and other hook dataCWnd* m_pWndInit;CWnd* m_pAlternateWndInit; / special case commdlg hookingDWORD m_dwPropStyle;DWORD m_dwPropExStyle;HWND m_hWndInit;BOOL m_

18、bDlgCreate;HHOOK m_hHookOldCbtFilter;HHOOK m_hHookOldMsgFilter;/ other CWnd modal dataMSG m_lastSentMsg; / see CWnd:WindowProcHWND m_hTrackingWindow; / see CWnd:TrackPopupMenuHMENU m_hTrackingMenu;TCHAR m_szTempClassName96; / see AfxRegisterWndClassHWND m_hLockoutNotifyWindow; / see CWnd:OnCommandBO

19、OL m_bInMsgFilter;/ other framework modal dataCView* m_pRoutingView; / see CCmdTarget:GetRoutingViewCFrameWnd*m_pRoutingFrame;/see CmdTarget:GetRoutingFrame/ MFC/DB thread-local dataBOOL m_bWaitForDataSource;/ common controls thread stateCToolTipCtrl* m_pToolTip;CWnd* m_pLastHit; / last window to ow

20、n tooltipint m_nLastHit; / last hittest codeTOOLINFO m_lastInfo; / last TOOLINFO structureint m_nLastStatus; / last flyby status messageCControlBar* m_pLastStatus; / last flyby status control bar/ OLE control thread-local dataCWnd* m_pWndPark; / "parking space" windowlong m_nCtrlRef; / ref

21、erence count on parking windowBOOL m_bNeedTerm; / TRUE if OleUninitialize needs to be called;從定義可以看出,線程狀態的成員數據分如下幾類:指向模塊狀態信息的指針,支持本線程的窗口創建的變量,MFC命令和消息處理用到的信息,處理工具條提示信息(tooltip)的結構,和處理OLE相關的變量,等等。AFX_MODULE_THREAD_STATE 該類定義如下:/ AFX_MODULE_THREAD_STATE (local to thread *and* module)class AFX_MOD

22、ULE_THREAD_STATE : public CNoTrackObjectpublic:AFX_MODULE_THREAD_STATE();virtual AFX_MODULE_THREAD_STATE();/ current CWinThread pointerCWinThread* m_pCurrentWinThread;/ list of CFrameWnd objects for threadCTypedSimpleList<CFrameWnd*> m_frameList;/ temporary/permanent map stateDWORD m_nTempMapL

23、ock; / if not 0, temp maps lockedCHandleMap* m_pmapHWND;CHandleMap* m_pmapHMENU;CHandleMap* m_pmapHDC;CHandleMap* m_pmapHGDIOBJ;CHandleMap* m_pmapHimageLIST;/ thread-local MFC new handler (separate from C-runtime)_PNH m_pfnNewHandler;#ifndef _AFX_NO_SOCKET_SUPPORT/ WinSock specific thread stateHWND

24、m_hSocketWindow;CMapPtrToPtr m_mapSocketHandle;CMapPtrToPtr m_mapDeadSockets;CPtrList m_listSocketNotifications;#endif;模塊-線程狀態的數據成員主要有:指向當前線程對象(CWinThread對象)的指針m_pCurrentWinThread;當前線程的框架窗口對象(CFrameWnd對象)列表m_frameList(邊框窗口在創建時(見圖5-8)把自身添加到m-frameList中,銷毀時則刪除掉,通過列表m_frameList可以遍歷模塊所有的邊框窗口);new操作的例外處理

25、函數m_pfnNewHandler;臨時映射鎖定標識m_nTempMapLock,防止并發修改臨時映射。系列Windows對象-MFC對象的映射,如m_pmapHWND等。這些數據成員都是線程和模塊私有的。下一節討論MFC如何通過上述這些類來實現其狀態的管理。線程局部存儲機制和狀態的實現 MFC實現線程、模塊或者線程-模塊私有狀態的基礎是MFC的線程局部存儲機制。MFC定義了CThreadSlotData類型的全局變量_afxThreadData來為進程的線程分配線程局部存儲空間:CThreadSlotData* _afxThreadData;在此基礎上,MFC定義了變量_afxTh

26、readState來管理線程狀態,定義了變量_afxBaseModuleState來管理進程狀態。THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)對于每個THREAD_LOCAL宏定義的變量,進程的每個線程都有自己獨立的拷貝,這個變量在不同的線程里頭可以有不同的取值。對于每個PROCESS_LOCAL宏定義的變量,每個進程都有自己獨立的拷貝,這個變量在不同的進程里頭可以有不同的取值。分別解釋這三個變量。CThreadSlotData

27、和_afxThreadData CThreadSlotData的定義 以Win32線程局部存儲機制為基礎,MFC設計了類CThreadSlotData來提供管理線程局部存儲的功能,MFC應用程序使用該類的對象全局變量_afxThreadData來管理本進程的線程局部存儲。CThreadSlotData類的定義如下:class CThreadSlotDatapublic:CThreadSlotData();/Operationsint AllocSlot();void FreeSlot(int nSlot);void* GetValue(int nSlot);void Set

28、Value(int nSlot, void* pValue);/ delete all values in process/threadvoid DeleteValues(HINSTANCE hInst, BOOL bAll = FALSE);/ assign instance handle to just constructed slotsvoid AssignInstance(HINSTANCE hInst);/ ImplementationDWORD m_tlsIndex;/ used to access system thread-local storageint m_nAlloc;

29、/ number of slots allocated (in UINTs)int m_nRover; / (optimization) for quick finding of free slotsint m_nMax; / size of slot table below (in bits)CSlotData* m_pSlotData; / state of each slot (allocated or not)/list of CThreadData structuresCTypedSimpleList<CThreadData*> m_list;CRITICAL_SECTI

30、ON m_sect;/ special version for threads only!void* GetThreadValue(int nSlot);void* PASCAL operator new(size_t, void* p) return p; void DeleteValues(CThreadData* pData, HINSTANCE hInst);CThreadSlotData();通過TLS索引m_tlsIndex,CThreadSlotData對象(_afxThreadData)為每一個線程分配一個線程私有的存儲空間并管理該空間。它把這個空間劃分為若干個槽,每個槽放一個

31、線程私有的數據指針,這樣每個線程就可以存放任意個線程私有的數據指針。CThreadSlotData的一些數據成員 在CThreadSlotData類的定義中所涉及的類或者結構定義如下:(1)m_sectm_sect是一個關鍵段變量,在_afxThreadData創建時初始化。因為_afxThreadData是一個全局變量,所以必須通過m_sect來同步多個線程對該變量的并發訪問。(2)m_nAlloc和m_pSlotDatam_nAlloc表示已經分配槽的數目,它代表了線程局部變量的個數。每一個線程局部變量都對應一個槽,每個槽對應一個線程局部變量。槽使用CSlotData類來管理。C

32、SlotData的定義如下:struct CSlotDataDWORD dwFlags; / slot flags (allocated/not allocated)HINSTANCE hInst; / module which owns this slot;該結構用來描述槽的使用:域dwFlags表示槽的狀態,即被占用或者沒有;域hInst表示使用該槽的模塊的句柄。m_pSlotData表示一個CSlotData類型的數組,用來描述各個槽。該數組通過成員函數AllocSlot和FreeSlot來動態地管理,見圖9-6。(3)m_list先討論CThreadData 類。CThreadData

33、定義如下:struct CThreadData : public CNoTrackObjectCThreadData* pNext; / required to be member of CSimpleListint nCount; / current size of pDataLPVOID* pData; / actual thread local data (indexed by nSlot);該結構用來描述CThreadSlotData為每個線程管理的線程局部空間:域pNext把各個線程的CThreadData項目鏈接成一個表,即把各個線程的線程私有空間鏈接起來;域nCount表示域pD

34、ata的尺寸,即存儲了多少個線程私有數據;pData表示一個LPVOID類型的數組,數組中的每一個元素保存一個指針,即線程私有數據指針,該指針指向一個在堆中分配的真正存儲線程私有數據的地址。數組元素的個數和槽的個數相同,每個線程局部變量(THREAD_LOCAL定義的變量)都有一個對應的槽號,用該槽號作為下標來引用pData。m_list表示一個CThreadData類型的指針數組,數組中的各項指向各個線程的線程私有空間,每個線程在數組中都有一個對應項。該數組通過GetValue、SetValue、DeleteValues等成員函數來管理,見圖9-6。_afxThreadData _

35、afxThreadData僅僅定義為一個CThreadSlotData類型的指針,所指對象在第一次被引用時創建,在此之前該指針為空。下文_afxThreadData含義是它所指的對象。圖9-5、9-6圖解了MFC的線程局部存儲機制的實現。圖9-5表示_afxTheadData使用TLS技術負責給進程分配一個TLS索引,然后使用TLS索引為進程的每一個線程分配線程局部存儲空間。圖9-6表示每個線程的的局部存儲空間可以分多個槽,每個槽可以放一個線程私有的數據指針。_afxThreadData負責給線程局部變量分配槽號并根據槽號存取數據。圖的左半部分描述了管理槽的m_pSlotData及類CSlot

36、Data的結構,右半部分描述了管理MFC線程私有空間的m_list及類CThreadData的結構。結合圖9-6,對MFC線程局部存儲機制總結如下:每個線程局部變量(宏THREAD_LOCAL定義)占用一個槽,并有一個槽號。 每個線程都有自己的MFC局部存儲空間(下文多次使用“線程的MFC局部存儲空間”,表示和此處相同的概念)。 通過TLS索引得到的是一個指針P1,它指向線程的MFC局部存儲空間。 通過指針P1和線程局部變量在空間所占用的槽號,得到該槽所存儲的線程私有的數據指針,即真正的線程私有數據的地址P2; 從地址P2得到數據D。 這個過程

37、相當于幾重間接尋址:先得到TLS線程私有數據指針,從TLS線程私有數據指針得到線程的MFC線程局部存儲空間,再從MFC局部存儲空間的對應槽得到一個線程私有的數據指針,從該指針得到最終的線程私有數據。如果沒有這種機制,使用Win32 TLS只要一次間接尋址:得到TLS線程私有數據指針,從該指針得到最終的線程私有數據。線程狀態_afxThreadState 從上一節知道了MFC的線程局部存儲機制。但有一點還不清楚,即某個線程局部變量所占用的槽號是怎么保存的呢?關于這點可從線程局部的線程狀態變量_afxThreadState的實現來分析MFC的作法。變量_afxThreadState的定義

38、如下:THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)THREAD_LOCAL 是一個宏,THREAD_LOCAL(class_name, ident_name)宏展開后如下:AFX_DATADEF CThreadLocal<class_name> ident_name;這里,CThreadLocal是一個類模板,從CThreadLocalObject類繼承。CThreadLocalObject和CThreadLocal的定義如下:class CThreadLocalObjectpublic:/ AttributesCNoTrackO

39、bject* GetData(CNoTrackObject* (AFXAPI*pfnCreateObject)();CNoTrackObject* GetDataNA();/ Implementationint m_nSlot;CThreadLocalObject();CThreadLocalObject用來幫助實現一個線程局部的變量。成員變量m_nSlot表示線程局部變量在MFC線程局部存儲空間中占據的槽號。GetDataNA用來返回變量的值。GetData也可以返回變量的值,但是如果發現還沒有給該變量分配槽號(m_slot=0),則給它分配槽號并在線程的MFC局部空間為之分配一個槽;如果在

40、槽m_nSlot還沒有數據(為空),則調用參數pfnCreateObject傳遞的函數創建一個數據項,并保存到槽m_nSlot中。template<class TYPE>class CThreadLocal : public CThreadLocalObject/ Attributespublic:inline TYPE* GetData()TYPE* pData = (TYPE*)CThreadLocalObject:GetData(&CreateObject);ASSERT(pData != NULL);return pData;inline TYPE* GetData

41、NA()TYPE* pData = (TYPE*)CThreadLocalObject:GetDataNA();return pData;inline operator TYPE*() return GetData(); inline TYPE* operator->() return GetData(); / Implementationpublic:static CNoTrackObject* AFXAPI CreateObject() return new TYPE; ;CThreadLocal模板用來聲明任意類型的線程私有的變量,因為通過模板可以自動的正確的轉化(cast)指針類

42、型。程序員可以使用它來實現自己的線程局部變量,正如MFC實現線程局部的線程狀態變量和模塊-線程變量一樣。CThrealLocal的成員函數CreateObject用來創建動態的指定類型的對象。成員函數GetData調用了基類CThreadLocalObject的同名函數,并且把CreateObject函數的地址作為參數傳遞給它。另外,CThreadLocal模板重載了操作符號“*”、“->”,這樣編譯器將自動地進行有關類型轉換,例如:_AFX_THREAD_STATE *pStata = _afxThreadState是可以被編譯器接收的。現在回頭來看_afxThreadState的定義

43、:從以上分析可以知道,THREAD_LOCAL(class_name, ident_name)定義的結果并沒有產生一個名為ident_name的class_name類的實例,而是產生一個CThreadLocal模板類(確切地說,是其派生類)的實例,m_nSlot初始化為0。所以,_afxThreadState實質上是一個CThreadLocal模板類的全局變量。每一個線程局部變量都對應了一個全局的CThreadLoacl模板類對象,模板對象的m_nSlot記錄了線程局部變量對象的槽號。進程模塊狀態afxBaseModuleState 進程模塊狀態定義如下:PROCESS_LOCAL(

44、_AFX_BASE_MODULE_STATE, _afxBaseModuleState)表示它是一個_AFX_BASE_MODULE_STATE類型的進程局部(process local)的變量。進程局部變量的實現方法主要是為了用于Win32s下。在Win32s下,一個DLL模塊如果被多個應用程序調用,它將讓這些程序共享它的全局數據。為了DLL的全局數據一個進程有一份獨立的拷貝,MFC設計了進程私有的實現方法,實際上就是在進程的堆(Heap)中分配全局數據的內存空間。在Win32下,DLL模塊的數據和代碼被映射到調用進程的虛擬空間,也就是說,DLL定義的全局變量是進程私有的;所以進程局部變量的

45、實現并不為Win32所關心。但是,不是說afxBaseModuleState不重要,僅僅是采用PROCESS_LOCAL技術聲明它是進程局部變量不是很必要了。PROCESS_LOCAL(class_name, ident_name)宏展開后如下:AFX_DATADEF CProcessLocal<class_name> ident_name;這里,CProcessLocal是一個類模板,從CProcessLocalObject類繼承。CProcessLocalObject和CProcessLocal的定義如下:class CProcessLocalObjectpublic:/ At

46、tributesCNoTrackObject* GetData(CNoTrackObject* (AFXAPI*pfnCreateObject)();/ ImplementationCNoTrackObject* volatile m_pObject;CProcessLocalObject();template<class TYPE>class CProcessLocal : public CProcessLocalObject/ Attributespublic:inline TYPE* GetData()TYPE* pData =(TYPE*)CProcessLocalObje

47、ct:GetData(&CreateObject);ASSERT(pData != NULL);return pData;inline TYPE* GetDataNA() return (TYPE*)m_pObject; inline operator TYPE*() return GetData(); inline TYPE* operator->() return GetData(); / Implementationpublic:static CNoTrackObject* AFXAPI CreateObject() return new TYPE; ;類似于線程局部對象,

48、每一個進程局部變量都有一個對應的全局CProcessLocal模板對象。狀態對象的創建 狀態對象的創建過程 回顧前一節的三個定義:CThreadSlotData* _afxThreadData;THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)第一個僅僅定義了一個指針;第二和第三個定義了一個模板類的實例。相應的CThreadSlotData對象(全局)、_AFX_THREAD_STATE對象(線程局部)以及_AFX

49、_BASE_MODULE_STATE對象(進程局部)并沒有創建。當然,模塊狀態對象的成員模塊-線程對象也沒有被創建。這些對象要到第一次被訪問時,才會被創建,這樣做會提高加載DLL的速度。下面以一個動態鏈接到MFC DLL的單模塊應用程序為例,說明這些對象的創建過程。當第一次訪問狀態信息時,比如使用 AfxGetModuleState得到模塊狀態,導致系列創建過程的開始,如圖9-7所示。首先分析語句pState=_afxThreadState。如果_afxThreadData、線程狀態和模塊狀態還沒有創建,該語句可以導致這些數據的創建。pState聲明為CNoTrackObject對象的指針,_

50、afxThreadState聲明為一個模板CThreadLocal的實例,pState=_afxThreadData為什么可以通過編譯器的檢查呢?因為CThreadLocal模板重載了操作符“”*”和“->”,這兩個運算返回CNoTrackObject類型的對象。回顧3.2節CThreadLocalObject、CThreadLocal的定義,這兩個操作符運算到最后都是調用CThreadLocalObject的成員函數GetData。創建_afxThreadData所指對象和線程狀態 CThreadLocalObject:GetData用來獲取線程局部變量(這個例子中是線程狀態

51、)的值,其參數用來創建動態的線程局部變量。圖9-7的上面的虛線框表示其流程:它檢查成員變量m_nSlot是否等于0(線程局部變量是否曾經被分配了MFC線程私有空間槽位),檢查全局變量_afxTheadData指針是否為空。如果_afxThreadData空,則創建一個CThreadSlotData類對象,讓_afxThreadData指向它,這樣本程序的MFC線程局部存儲的管理者被創建。如果m_nSlot等于0,則讓_afxThreadDtata調用AllocSlot分配一個槽位并把槽號保存在m_nSlot中。得到了線程局部變量(線程狀態)所占用的槽位后,委托_afxThreadData調用G

52、etThreadValue(m_nSlot)得到線程狀態值(指針)。如果結果非空,則返回它;如果結果是NULL,則表明該線程狀態還沒有被創建,于是使用參數創建一個動態的線程狀態,并使用SetValue把其指針保存在槽m_nSlot中,返回該指針。創建模塊狀態 得到了線程狀態的值后,通過它得到模塊狀態m_pModuleState。如果m_pModuleState為空,表明該線程狀態是才創建的,其許多成員變量還沒有賦值,程序的進程模塊狀態還沒有被創建。于是調用函數_afxBaseModule.GetData,導致進程模塊狀態被創建。圖9-7的下面一個虛線框表示了CProcessLocal

53、Object:GetData的創建過程:_afxBaseModule首先檢查成員變量m_pObject是否空,如果非空就返回它,即進程模塊狀態指針;否則,在堆中創建一個動態的_AFX_BASE_MODULE_STATE對象,返回。從上述兩個GetData的實現可以看出,CThreadLocal模板對象負責線程局部變量的創建和管理(查詢,修改,刪除);CProcessLocal模板對象負責進程局部變量的創建和管理(查詢,修改,刪除)。模塊-線程狀態的創建 模塊狀態的成員模塊-線程狀態m_thread的創建類似于線程狀態的創建:當第一次訪問m_thread所對應的CThreadLocal

54、模板對象時,給m_thread分配MFC線程局部存儲的私有槽號m_nSlot,并動態地創建_AFX_MODULE_THREAD_STATE對象,保存對象指針在m_nSlot槽中。創建過程所涉及的幾個重要函數的算法 創建過程所涉及的幾個重要函數的算法描述如下:AllocSlot AllocSlot用來分配線程的MFC私有存儲空間的槽號。由于該函數要修改全局變量_afxThreadData,所以必須使用m_sect關鍵段對象來同步多個線程對該函數的調用。CThreadSlotData:AllocSlot()進入關鍵段代碼(EnterCriticalSection(m_sect)

55、;)搜索m_pSlotData,查找空槽(SLOT)如果不存在空槽(第一次進入時,肯定不存在)分配或再分配內存以創建新槽,指針m_pSlotData指向分配的地址。得到新槽(SLOT)標志該SLOT為已用記錄最新可用的SLOT到成員變量m_nRover中。離開關鍵段代碼(LeaveCriticalSection(m_sect);)返回槽號GetThreadValue GetThreadValue用來獲取調用線程的第slot個線程局部變量的值。每一個線程局部變量都占用一個且只一個槽位。CThreadSlotData:GetThreadValue(int slot)/得到一個CThrea

56、dData型的指針pData/pData指向MFC線程私有存儲空間。/m_tlsIndex在_afxThreadData創建時由構造函數創建pData=(CThreadData*)TlsGetValue(m_tlsIndex),。如果指針空或slot>pData->nCount, 則返回空。否則,返回pDataSetValue SetValue用來把調用線程的第slot個線程局部變量的值(指針)存放到線程的MFC私有存儲空間的第slot個槽位。CThreadSlotData:SetValue(int slot, void *pValue)/通過TLS索引得到線程的MFC私

57、有存儲空間pData = (CThreadData*)TlsGetValue(m_tlsIndex)/沒有得到值或者pValue非空且當前槽號,即/線程局部變量的個數/大于使用當前局部變量的線程個數時if (pData NULL or slot > pData->nCount && pValue!=NULL)if pData NULL /當前線程第一次訪問該線程局部變量創建一個CThreadData實例;添加到CThreadSlotData:m_list;令pData指向它;按目前為止,線程局部變量的個數為pData->pData分配或重分配內存,用來容納指向

58、真正線程數據的指針調用TlsSetValue(pData)保存pData/把指向真正線程數據的pValue保存在pData對應的slot中pData->pDataslot = pValue管理狀態 在描述了MFC狀態的實現機制之后,現在來討論MFC的狀態管理和相關狀態的作用。模塊狀態切換 模塊狀態切換就是把當前線程的線程狀態的m_pModuleState指針指向即將運行模塊的模塊狀態。MFC使用AFX_MANAGE_STATE宏來完成模塊狀態的切換,即進入模塊時使用當前模板的模板狀態,并保存原模板狀態;退出模塊時恢復原來的模塊狀態。這相當于狀態的壓棧和出棧。實現原理如

59、下。先看MFC關于AFX_MANAGE_STATE的定義:#ifdef _AFXDLLstruct AFX_MAINTAIN_STATEAFX_MAINTAIN_STATE(AFX_MODULE_STATE* pModuleState);AFX_MAINTAIN_STATE();protected:AFX_MODULE_STATE* m_pPrevModuleState;/AFX_MANAGE_STATE宏的定義:#define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE _ctlState(p);#else / _AFXDLL#define AFX_MANA

60、GE_STATE(p)#endif /!_AFXDLL如果使用MFC DLL,MFC提供類AFX_MAINTAIN_STATE來實現狀態的壓棧和出棧,AFX_MANAGE_SATATE宏的作用是定義一個AFX_MAINTAIN_STATE類型的局部變量_ctlState。AFX_MAINTAIN_STATE的構造函數在其成員變量m_pPrevModuleState中保存當前的模塊狀態對象,并把參數指定的模塊狀態設定為當前模塊狀態。所以該宏作為入口點的第一條語句就切換了模塊狀態。在退出模塊時,局部變量_ctlState將自動地銷毀,這導致AFX_MAINTAIN_STATE的析構函數被調用,析構函數把保存在m_pPrevModuleState的狀態設置為當前狀態。AFX_MANAGE_SATATE的參數在不同場合是不一樣的,例如,DLL的輸出函數使用 AFX_MANAGE_SATATE(AfxGetStaticMo

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論