In the MFC environment, normally, thread should be launched with AfxBeginThread for taking usage of MFC multiple-thread mechanism; In such mechanism, those datastructures, such as AFX_MODULE_STATE, would be used by MFC framework to maintain related thread information. It runs well when threads, launched with AfxBeginThread, quit before the main thread, which is responsible for initializing C run-time, but if such main thread quit before any other thread launched by AfxBeginThread, the current application would crash.
Such crash comes from the _afxThreadData (CThreadSlotData* _afxThreadData, which is defined in AFXTLS.cpp as global data structure) has been destructed while the main thread quits and it will invoke related function to clean up global data structures, including _afxThreadData definitely. Consequently, serious developer should prepare for such case (other worker thread quits before main thread). The reasonable resolve for such issue, would ensure any other threads should quit before the main thread. .h file ///////////////////////////////////////////////////////////////////////////// // CSafeEnterLeaveThread thread class CSafeEnterLeaveThread : public CWinThread { DECLARE_DYNCREATE(CSafeEnterLeaveThread) protected: CSafeEnterLeaveThread(); // protected constructor used by dynamic creation // Attributes public: // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CSafeEnterLeaveThread) public: virtual BOOL InitInstance(); virtual int ExitInstance(); //}}AFX_VIRTUAL // Implementation protected: virtual ~CSafeEnterLeaveThread(); // Generated message map functions //{{AFX_MSG(CSafeEnterLeaveThread) // NOTE - the ClassWizard will add and remove member functions here. //}}AFX_MSG DECLARE_MESSAGE_MAP() }; .cpp file ///////////////////////////////////////////////////////////////////////////// // CSafeEnterLeaveThread IMPLEMENT_DYNCREATE(CSafeEnterLeaveThread, CWinThread) CSafeEnterLeaveThread::CSafeEnterLeaveThread() { } CSafeEnterLeaveThread::~CSafeEnterLeaveThread() { } BOOL CSafeEnterLeaveThread::InitInstance() { // TODO: perform and per-thread initialization here ASSERT(this->m_hThread); CMainApp::RegisterMFCThread(this->m_hThread); return TRUE; } int CSafeEnterLeaveThread::ExitInstance() { // TODO: perform any per-thread cleanup here ASSERT(this->m_hThread); CMainApp::UnRegisterMFCThread(this->m_hThread); return CWinThread::ExitInstance(); } BEGIN_MESSAGE_MAP(CSafeEnterLeaveThread, CWinThread) //{{AFX_MSG_MAP(CSafeEnterLeaveThread) // NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP END_MESSAGE_MAP() And in the CMainApp, set<HANDLE> g_ThreadHandleSet; HANDLE g_ThreadHandleArray[MAXIMUM_WAIT_OBJECTS]; CCriticalSection g_csGlobalData; void CAccgbApp::CheckAllOtherMFCThreadsLeave() { int count = g_ThreadHandleSet.size(); if (count == 0) return; set<HANDLE>::iterator it; int idx = 0; for (it = g_ThreadHandleSet.begin(); it != g_ThreadHandleSet.end() && idx < MAXIMUM_WAIT_OBJECTS; it++, idx++) { g_ThreadHandleArray[idx] = *it; } if (count > idx) count = idx; ::WaitForMultipleObjects(count, g_ThreadHandleArray, TRUE, INFINITE); } void CAccgbApp::CleanupGlobalData() { g_csGlobalData.Lock(); g_ThreadHandleSet.empty(); g_csGlobalData.Unlock(); } BOOL CAccgbApp::RegisterMFCThread(HANDLE hThread) { if (hThread == NULL) return FALSE; g_csGlobalData.Lock(); if (g_ThreadHandleSet.find(hThread) == g_ThreadHandleSet.end()) g_ThreadHandleSet.insert(hThread); g_csGlobalData.Unlock(); return TRUE; } void CAccgbApp::UnRegisterMFCThread(HANDLE hThread) { if (hThread == NULL) return; g_csGlobalData.Lock(); if (g_ThreadHandleSet.find(hThread) != g_ThreadHandleSet.end()) g_ThreadHandleSet.erase(hThread); g_csGlobalData.Unlock(); } 摘自 唐亮的個人技術博客