上一篇文章《从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