MFC的工作线程揭秘-CWinThead自动删除揭秘

来源:互联网 发布:所以木工软件 编辑:程序博客网 时间:2024/05/22 19:16

呵呵,说揭秘其实是说给自己听的,因为今天做了个MFC多线程优先级的例子,在线程里面使用了AfxGetMainWnd()->MDIGetActive(),返回值类型暂不讨论,就是这套东西在线程中具有诡异现象,查了一点说是线程里面有个map记录了窗口指针和其句柄的映射关系,使得有些指针不好使。等查明后在补全这里。

 

今天主要写的是另一个问题,创建工作线程时,AfxBeginThread返回的CWinThread的对象指针所指的对象在默认情况下会自动删除。如果你不想让他自己删除,而且自己想查看线程信息的话,只需要在创建之后,用返回的CWinThread对象指针来设置m_bAutoDelete成员变量为FALSE即可。

 

而我很好奇的是,为什么返回的CWinThread可以自动删除呢?

其实光看别人说的自己知道遍罢,其实《深入浅出MFC》中侯老爷子谆谆教导:“在线程结束时,记得把该对象释放掉(利用delete)。”,就是因为这句话,让我的程序中断好几次,错误居然是销毁已经清理了的内存空间。后来查最新资料才知道,默认是会自动删除的。可能侯老爷子当时版本太低,或者其他原因吧。但是侯老爷子说过:“学一样东西,只知道怎么用,但不明白其中的道理,实在是不高明”。再加上本身我就是遇到问题非得心里有底才行,光听别人说是这样,不明白为什么,总觉得心里用的不踏实。好了,废话就不说了,切入正题。

 

为什么AfxBeginThread函数动态创建的对象会在线程结束后自动销毁呢?难道是有什么消息映射?任何东西究其根源离不开他本身,先去看看AfxbeginThread的源码:

[cpp] view plaincopy
  1. CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,  
  2.     int nPriority, UINT nStackSize, DWORD dwCreateFlags,  
  3.     LPSECURITY_ATTRIBUTES lpSecurityAttrs)  
  4. {  
  5. #ifndef _MT  
  6.     pfnThreadProc;  
  7.     pParam;  
  8.     nPriority;  
  9.     nStackSize;  
  10.     dwCreateFlags;  
  11.     lpSecurityAttrs;  
  12.   
  13.     return NULL;  
  14. #else  
  15.     ASSERT(pfnThreadProc != NULL);  
  16.   
  17.     CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);  
  18.     ASSERT_VALID(pThread);  
  19.   
  20.     if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,  
  21.         lpSecurityAttrs))  
  22.     {  
  23.         pThread->Delete();  
  24.         return NULL;  
  25.     }  
  26.     VERIFY(pThread->SetThreadPriority(nPriority));  
  27.     if (!(dwCreateFlags & CREATE_SUSPENDED))  
  28.         VERIFY(pThread->ResumeThread() != (DWORD)-1);  
  29.   
  30.     return pThread;  
  31. #endif //!_MT)  
  32. }  

 

没有发现什么端倪,一切很合情合理。但是书上说过AfxEndThread()就像,_beginthread和_endthread一样配对。那让我们去看一下AfxEndThread()。下面是源码:

[cpp] view plaincopy
  1. void AFXAPI AfxEndThread(UINT nExitCode, BOOL bDelete)  
  2. {  
  3. #ifndef _MT  
  4.     nExitCode;  
  5.     bDelete;  
  6. #else  
  7.     // remove current CWinThread object from memory  
  8.     AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();  
  9.     CWinThread* pThread = pState->m_pCurrentWinThread;  
  10.     if (pThread != NULL)  
  11.     {  
  12.         ASSERT_VALID(pThread);  
  13.         ASSERT(pThread != AfxGetApp());  
  14.   
  15.         // cleanup OLE if required  
  16.         if (pThread->m_lpfnOleTermOrFreeLib != NULL)  
  17.             (*pThread->m_lpfnOleTermOrFreeLib)(TRUE, FALSE);  
  18.   
  19.         if (bDelete)  
  20.             pThread->Delete();  
  21.         pState->m_pCurrentWinThread = NULL;  
  22.     }  
  23.   
  24.     // allow cleanup of any thread local objects  
  25.     AfxTermThread();  
  26.   
  27.     // allow C-runtime to cleanup, and exit the thread  
  28.     _endthreadex(nExitCode);  
  29. #endif //!_MT  
  30. }  

其中我们看到bDelete,原来删除工作都在这呢,看:

if(bDelete)

    pThread->Delete();

 

那么我们去看看CWinThread::Delete():

[cpp] view plaincopy
  1. void CWinThread::Delete()  
  2. {  
  3.     // delete thread if it is auto-deleting  
  4.     if (m_bAutoDelete)  
  5.         delete this;  
  6. }  

 

原来,如果m_bAutoDelete是真时,这个函数内部会将自己删除。

这么看来,我们可以大胆猜测,肯定是线程函数结束后自动调用了这个函数。而我们看的这几个代码,全然没有。但是我们还有一个重要的函数没有看:那么就AfxBeginThread里面的CWinThread::CreateThread。让我们去看看:

 

[cpp] view plaincopy
  1. BOOL CWinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize,  
  2.     LPSECURITY_ATTRIBUTES lpSecurityAttrs)  
  3. {  
  4. #ifndef _MT  
  5.     dwCreateFlags;  
  6.     nStackSize;  
  7.     lpSecurityAttrs;  
  8.   
  9.     return FALSE;  
  10. #else  
  11.     ENSURE(m_hThread == NULL);  // already created?  
  12.   
  13.     // setup startup structure for thread initialization  
  14.     _AFX_THREAD_STARTUP startup; memset(&startup, 0, sizeof(startup));  
  15.     startup.pThreadState = AfxGetThreadState();  
  16.     startup.pThread = this;  
  17.     startup.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);  
  18.     startup.hEvent2 = ::CreateEvent(NULL, TRUE, FALSE, NULL);  
  19.     startup.dwCreateFlags = dwCreateFlags;  
  20.     if (startup.hEvent == NULL || startup.hEvent2 == NULL)  
  21.     {  
  22.         TRACE(traceAppMsg, 0, "Warning: CreateEvent failed in CWinThread::CreateThread./n");  
  23.         if (startup.hEvent != NULL)  
  24.             ::CloseHandle(startup.hEvent);  
  25.         if (startup.hEvent2 != NULL)  
  26.             ::CloseHandle(startup.hEvent2);  
  27.         return FALSE;  
  28.     }  
  29.   
  30.     // create the thread (it may or may not start to run)  
  31.     m_hThread = (HANDLE)(ULONG_PTR)_beginthreadex(lpSecurityAttrs, nStackSize,    
  32.         &_AfxThreadEntry, &startup, dwCreateFlags | CREATE_SUSPENDED, (UINT*)&m_nThreadID);  
  33.     if (m_hThread == NULL)  
  34.     {  
  35.         ::CloseHandle(startup.hEvent);  
  36.         ::CloseHandle(startup.hEvent2);  
  37.         return FALSE;  
  38.     }  
  39.   
  40.         ...  
  41. #endif //!_MT  
  42. }  

 

其中一些与本问题关联不大的代码我就...,你懂的。其中的_beginthreadex最为关键,你看它传递的函数地址居然是_AfxThreadEntry这个鬼东西。还传递了一个startup(它主要做线程初始化工作的)。居然没有到我们指定函数地址去。无论什么线程函数,用mfc创建的时候先进入它自己的线程。那我们心里面就有点闪光了,莫非mfc在_AfxThreadEntry里面...,对,你懂滴。

 

让我们看看这个_AfxThreadEntry鬼东西:

[cpp] view plaincopy
  1. UINT APIENTRY _AfxThreadEntry(void* pParam)  
  2. {  
  3.     _AFX_THREAD_STARTUP* pStartup = (_AFX_THREAD_STARTUP*)pParam;  
  4.     ASSERT(pStartup != NULL);  
  5.     ASSERT(pStartup->pThreadState != NULL);  
  6.     ASSERT(pStartup->pThread != NULL);  
  7.     ASSERT(pStartup->hEvent != NULL);  
  8.     ASSERT(!pStartup->bError);  
  9.   
  10.     CWinThread* pThread = pStartup->pThread;  
  11.     CWnd threadWnd;  
  12.     TRY  
  13.     {  
  14.         // inherit parent's module state  
  15.         _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();  
  16.         pThreadState->m_pModuleState = pStartup->pThreadState->m_pModuleState;  
  17.   
  18.         // set current thread pointer for AfxGetThread  
  19.         AFX_MODULE_STATE* pModuleState = AfxGetModuleState();  
  20.         pThread->m_pModuleState = pModuleState;  
  21.         AFX_MODULE_THREAD_STATE* pState = pModuleState->m_thread;  
  22.         pState->m_pCurrentWinThread = pThread;  
  23.   
  24.         // forced initialization of the thread  
  25.         AfxInitThread();  
  26.   
  27.         // thread inherits app's main window if not already set  
  28.         CWinApp* pApp = AfxGetApp();  
  29.         if (pApp != NULL &&   
  30.             pThread->m_pMainWnd == NULL && pApp->m_pMainWnd->GetSafeHwnd() != NULL)  
  31.         {  
  32.             // just attach the HWND  
  33.             threadWnd.Attach(pApp->m_pMainWnd->m_hWnd);  
  34.             pThread->m_pMainWnd = &threadWnd;  
  35.         }  
  36.     }  
  37.     CATCH_ALL(e)  
  38.     {  
  39.         // Note: DELETE_EXCEPTION(e) not required.  
  40.   
  41.         // exception happened during thread initialization!!  
  42.         TRACE(traceAppMsg, 0, "Warning: Error during thread initialization!/n");  
  43.   
  44.         // set error flag and allow the creating thread to notice the error  
  45.         threadWnd.Detach();  
  46.         pStartup->bError = TRUE;  
  47.         VERIFY(::SetEvent(pStartup->hEvent));  
  48.         AfxEndThread((UINT)-1, FALSE);  
  49.         ASSERT(FALSE);  // unreachable  
  50.     }  
  51.     END_CATCH_ALL  
  52.   
  53.         ......  
  54.     // first -- check for simple worker thread  
  55.     DWORD nResult = 0;  
  56.     if (pThread->m_pfnThreadProc != NULL)  
  57.     {  
  58.         nResult = (*pThread->m_pfnThreadProc)(pThread->m_pThreadParams);  
  59.         ASSERT_VALID(pThread);  
  60.     }  
  61.     // else -- check for thread with message loop  
  62.     else if (!pThread->InitInstance())  
  63.     {  
  64.         ASSERT_VALID(pThread);  
  65.         nResult = pThread->ExitInstance();  
  66.     }  
  67.     else  
  68.     {  
  69.         // will stop after PostQuitMessage called  
  70.         ASSERT_VALID(pThread);  
  71.         nResult = pThread->Run();  
  72.     }  
  73.   
  74.     // cleanup and shutdown the thread  
  75.     threadWnd.Detach();  
  76.     AfxEndThread(nResult);  
  77.   
  78.     return 0;   // not reached  
  79. }  

 

你看58行:

nResult = (*pThread->m_pfnThreadProc)(pThread->m_pThreadParams);

还有最后那行:

AfxEndThread(nResult);

 

原来啊,我们自己的线程函数在mfc的线程函数成了函数调用了。呵呵。最后这个AfxEndThread(nResult),就是我们一直找的。

其实我们可以在自己的线程里直接用AfxEngThread来清理CWinThread对象,这样mfc的线程自己在调用的AfxEndThread的时候,里面有自检机制,就不会再销毁一次了。

 

源码说明一切。侯老爷子说的。

0 0
原创粉丝点击