前言:
近日在網上看到很多人問及如何關閉一下執行緒,但是我看網上給出的並不詳細,而且有些方法還是錯誤的。小弟在此拙作一篇,不談別的,只談及如何正確的關閉MFC的執行緒,至於Win32和C RunTime的執行緒暫不涉及。
一.關於MFC的執行緒
1.MFC的執行緒有兩種,一種稱為Work執行緒,一種稱為UI執行緒。一般情況下Work執行緒與UI執行緒的區別主要在於UI執行緒有訊息佇列(並不是有沒有介面,這點要注意,UI執行緒也是可以沒有介面的)。
2.建立這兩種執行緒的區別也不大,可以從建立函式看出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// Work執行緒 CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ); // UI執行緒 CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ); |
關於函式的具體使用,請查閱MSDN,這裡不涉及。
二. 結束執行緒前的注意事項
在結束一個執行緒前,只有一點要注意,那就是m_bAutoDelete 的狀態。(什麼?不知道m_bAutoDelete ?!!快去查閱MSDN吧)。
1 2 3 |
m_bAutoDelete = FALSE; // 表示你自己管理 CWind 物件,包括它的清理 m_bAutoDelete = TRUE; // 預設值, 系統會自己清理 CWind 物件 |
m_bAutoDelete = TRUE; 系統自己清理CWind物件,當然還包括CloseHandle(),ExitInstance()等等一堆函式的呼叫。
m_bAutoDelete = FALSE; 那麼就一定要記得自己在用完後呼叫delete刪除建立執行緒的物件,這一點極為重要,因為不呼叫delete一定會有記憶體洩漏問題。
總之m_bAutoDelete 的值對結束工作是很重要的,這點一定要注意。
三.正確的結束一個Work執行緒
因為Work執行緒是一個全域性函式,或者是一個Static函式,所以它的執行完成也就是它的正常退出了。(什麼?不明白,示例程式碼如下)
1.情況一:
1 2 3 4 5 6 |
UINT WorkFunc(LPVOID pParam) { // 工作 ...... return 0; // 就算正常退出了,簡單吧 } |
2.情況二:
Work執行緒是個死迴圈或一時半會兒出不來,這時要主執行緒要發個訊息給Work執行緒,讓他退出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
UINT WorkFunc(LPVOID pParam) { for(;;) { // ... if( WAIT_OBJECT_0 == WaitForSingleObject(m_hThread, INFINITE)} // 收到激發態的訊息 { return 0;//正常退出 } }//end for return 0; } |
關於主執行緒發一個激發態的訊息給Work執行緒,有多種方法,如在主執行緒裡呼叫SetEvent()等等,你想用什麼都行,但是最好不要在Work執行緒裡用Busy loop的方法。至於為什麼,請參閱《Win32多執行緒程式設計》上面的論述。
四.正確結束一個UI執行緒
因為UI執行緒有訊息佇列,所以結束一個UI執行緒最好的方法是發一個WM_QUIT訊息給訊息佇列,方法很多如:PostQuitMessage(),PostThreadMessage()等等。但是發出訊息後最好等待看UI執行緒是否已經退出(很多人都沒有提及這一點,但是實際工作中發現,加上這一點是多麼的重要)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 主執行緒結束UI執行緒的程式碼 if(pThread) { // 1. 發一個WM_QUIT 訊息結 UI 執行緒 pThread->PostThreadMessage(WM_QUIT, NULL, NULL); // 2. 等待 UI 執行緒正常退出 if (WAIT_OBJECT_0 == WaitForSingleObject(pThread->m_hThread, INFINITE)) { // 3. 刪除 UI 執行緒物件,只有當你設定了m_bAutoDelete = FALSE; 時才呼叫 delete pThread; } } |
五.關於幾個問題的解答
1.問:為什麼我的UI執行緒沒有呼叫ExitInstance()?
答:最大的可能是你的WM_QUIT訊息沒有通知到UI執行緒。為了保險期間最好呼叫PostThreadMessage(),這樣可以指定執行緒的ID。當然如果你對訊息比較熟悉的話,也可以拋一個訊息到最頂層。
2.問:為什麼我的UI執行緒沒有呼叫解構函式?
答:檢檢視你的m_bAutoDelete = FALSE,如果是的話,那麼看你的執行緒物件是否已經delete了。一般情況下呼叫delete會呼叫解構函式。
3.問:在UI執行緒中沒有呼叫WaitForSingleObject(),會怎麼樣?
答:我們知道在PostMessage()之後,函式會馬上返回,如查沒有wait…(),那麼緊接著就呼叫了delete,很有可能物件做的退出操作過程還沒有完成時,又把物件delete掉了,結果還是沒有正常結束。(注:WM_QUIT訊息之後會觸發一堆函式,這個時間是不定的,所以最好Wait…才是正道。)
六.最後不願提的函式
幾乎每本講執行緒的書都會提到下面的函式:
void AfxEndThread(UINT nExitCode);
TerminateThread();
……還有其它的一些極端的函式
我的觀點是:最好不要使用,除非你知道要發生什麼!!