上一篇文章《從win32到MFC(一)前言》介紹了MFC的入口函數,有一段代碼:
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
初次讀到這兩行代碼還是比較混亂,可以推斷AfxGetThread()和AfxGetApp()獲得的CWinThread和CWinApp對象已經在入口函數執行前完成了構造。
寫過MFC程序的開發者應該可以聯想到全局變量 CxxxApp theApp,把AfxGetApp()與全局變量 theApp關聯起來,AfxGetApp()獲取的正是這個對象。
CWinApp的構造:
CWinApp::CWinApp(LPCTSTR lpszAppName) { ...... // 初始化 CWinThread 狀態 AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE(); ENSURE(pModuleState); AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread; ENSURE(pThreadState); ASSERT(AfxGetThread() == NULL); pThreadState->m_pCurrentWinThread = this; ASSERT(AfxGetThread() == this); m_hThread = ::GetCurrentThread(); m_nThreadID = ::GetCurrentThreadId(); // 初始化 CWinApp 狀態 ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please pModuleState->m_pCurrentWinApp = this; ASSERT(AfxGetApp() == this); ...... }
AfxGetThread 和 AfxGetApp 的實現:
CWinThread* AFXAPI AfxGetThread() { AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); CWinThread* pThread = pState->m_pCurrentWinThread; return pThread; }
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() { return afxCurrentWinApp; }
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp
通過以上代碼的實現,可以清晰的看到 AfxGetThread 和 AfxGetApp 獲得的指針對象保存在 AFX_MODULE_THREAD_STATE中,由CWinApp在構造函數中初始化。CWinApp繼承自CWinThread,AfxGetThread 和 AfxGetApp 得到的其實是同一個對象。
以上代碼的分析其實只考慮了主線程的情況,事實上在多線程的環境中其他線程執行 AfxGetThread 獲取到的並不是CWinApp,而是當前線程的對象,繼續往下分析 AfxGetModuleThreadState。
AfxGetModuleThreadState的實現:
AFX_MODULE_THREAD_STATE* AFXAPI AfxGetModuleThreadState() { AFX_MODULE_THREAD_STATE* pResult=AfxGetModuleState()->m_thread.GetData(); ENSURE(pResult != NULL); return pResult; }
AfxGetModuleThreadState 進一步調用 AfxGetModuleState 獲取 AFX_MODULE_THREAD_STATE 指針對象。
AfxGetModuleState的實現:
AFX_MODULE_STATE* AFXAPI AfxGetModuleState() { _AFX_THREAD_STATE* pState = _afxThreadState; ENSURE(pState); AFX_MODULE_STATE* pResult; if (pState->m_pModuleState != NULL) { // thread state's module state serves as override pResult = pState->m_pModuleState; } else { // otherwise, use global app state pResult = _afxBaseModuleState.GetData(); } ENSURE(pResult != NULL); return pResult; }
AfxGetModuleState 的實現也很簡潔,我們繼續往下分析全局對象 _afxThreadState (在afxstate.cpp 143行)
THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
#define THREAD_LOCAL(class_name, ident_name) \
AFX_COMDAT CThreadLocal<class_name> ident_name;
THREAD_LOCAL宏定義展開其實是一個模板類的定義 CThreadLocal。
template<class TYPE> class CThreadLocal : public CThreadLocalObject { // Attributes public: AFX_INLINE TYPE* GetData() { TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject); ENSURE(pData != NULL); return pData; } AFX_INLINE TYPE* GetDataNA() { TYPE* pData = (TYPE*)CThreadLocalObject::GetDataNA(); return pData; } AFX_INLINE operator TYPE*() { return GetData(); } AFX_INLINE TYPE* operator->() { return GetData(); } // Implementation public: static CNoTrackObject* AFXAPI CreateObject() { return new TYPE; } };
可以看到 CThreadLocal 重載了 operator ->,最終調用了基類 CThreadLocalObject 的函數 GetData 來獲取 _AFX_THREAD_STATE,閱讀到這里終於快搞清 CWinThread 的真面目,我們繼續往下分析。
CThreadLocalObject::GetData 的實現:
CNoTrackObject* CThreadLocalObject::GetData( CNoTrackObject* (AFXAPI* pfnCreateObject)()) { ENSURE(pfnCreateObject);
// 初次調用先構造 CThreadSlotData if (m_nSlot == 0) { if (_afxThreadData == NULL) { _afxThreadData = new(__afxThreadData) CThreadSlotData; ENSURE(_afxThreadData != NULL); } m_nSlot = _afxThreadData->AllocSlot(); ENSURE(m_nSlot != 0); } CNoTrackObject* pValue = static_cast<CNoTrackObject*>(_afxThreadData->GetThreadValue(m_nSlot)); if (pValue == NULL) { // allocate zero-init object pValue = (*pfnCreateObject)(); // set tls data to newly created object _afxThreadData->SetValue(m_nSlot, pValue); ASSERT(_afxThreadData->GetThreadValue(m_nSlot) == pValue); } return pValue; }
最終通過 afxThreadData 來獲取對象,Windows開發者應該對TLS(線程局部存儲)的概念並不陌生,第一次讀到這里想到了MFC應該通過 TLS 存儲線程相關的環境上下文。
感興趣的讀者可以自行閱讀類 CThreadSlotData 的實現細節,這里不再詳細分析。
總結:
貼了這么多代碼,簡單的總結下流程:
AfxGetApp / AfxGetThread -> AfxGetModuleThreadState -> AfxGetModuleState -> CThreadLocalObject::GetData (_afxThreadState operator->) -> CThreadSlotData::GetThreadValue
