安全終止MFC線程

終止線程 
有兩種狀況可使線程結束:控制函數結束或者根本就不容許線程完成,而提早終止它。咱們能夠想象在WORD中進行後臺打印,若是打印結束了,那線程就能夠結束了。若是用戶停止了打印,那後臺打印線程也要終止了。本文將主要介紹對這兩種狀況的實現,而且介紹如何得到線程的結束代碼。 

1.對於工做線程,結束它是比較容易的:退出線程函數而後返回一個結束緣由的代碼就是了。用戶可使用AfxEndThread函數或直接利用return返回。一般0表明成功返回,這不是硬性規定,一切要取決於你了。對於用戶界面線程,調用::PostQuitMessage,它所要的惟一的參數就是返回代碼,也就是工做線程中的那個碼,性質是同樣的。0一般表明成功。 

2.提早終止一個線程也不難:在線程函數中調用AfxEndThread就是了,其中要傳入的參數就是返回代碼。這會中止線程的執行,釋放線程棧,及與線程相關的DLL,並從內存中刪除線程對象。AfxEndThread必須在線程函數內調用,若是用戶但願從一個線程結束另外一個線程,則須要在兩個線程間創建通訊機制。 

若是須要得到線程返回代碼,只須要調用::GetExitCodeThread就能夠了。這個函數的具體做用就看你們具體去查幫助了。它傳入的是線程的句柄,和一個提向返回代碼的指針。未來就從那個指針獲得返回代碼。若是線程仍然處於活動狀態,那麼::GetExitCodeThread獲得的返回代碼爲STILL_ACTIVE,若是已經退出則獲得的是返回代碼的地址。得到CWinThread對象的返回代碼還須要一點麻煩,一般,當CWinThread線程結束時,線程對象就刪除了,由於這個對象不存在了,也就沒有辦法訪問對象的m_hThread變量了,爲了不這種狀況,能夠有兩種方法: 

將m_bAutoDelete設置爲FALSE,這使得線程結束後CWinThread對象仍然存在,這樣用戶就能夠訪問m_hThread了,可是若是用戶使用這種方法,用戶須要本身析構CWinThread對象。這種方法是推薦的方法。 

下一個方法是另外保存線程的句柄。在線程建立後,將m_hThread保存在另外一個變量中,之後訪問這個變量就是了。可是要當心,在複製句柄之前線程並無結束,最安全的方法是在AfxBeginThread中傳入CREATE_SUSPENDED,保存句柄,而後經過調用ResumeThread,從新開始線程。這兩種方法均可以幫助用戶獲得CWinThread對象的返回代碼。安全

對於Worker線程,終止線程可使用線程的退出碼做爲返回值從線程函數返回。數據結構

對於UI線程,由於有消息循環,須要發送一個WM_QUIT消息到線程的消息隊列,當線程接收到WM_QUIT消息時退出消息循環。所以,結束線程能夠在線程內部調用SDK的PostQuitMessage函數,發送WM_QUIT消息。
PostQuitMessage函數的定義以下:函數

void PostQuitMessage(int nExitCode);

其中:ui

nExitCode:線程的退出碼。spa

MFC還提供了AfxEndThread函數,Worker線程和UI線程均可以經過在線程內部調用AfxEndThread函數結束線程。線程

AfxEndThread函數的定義以下:指針

void AfxEndThread(UINT nExitCode, BOOL bDelete = TRUE);

其中:code

nExitCode:線程的退出碼。對象

在MFC的THRDCORE.CPP中,AfxEndThread函數的相關代碼以下:blog

// THRDCORE.CPP
void AFXAPI AfxEndThread(UINT nExitCode, BOOL bDelete)
{
    // remove current CWinThread object from memory
    AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
    CWinThread* pThread = pState->m_pCurrentWinThread;
    if (pThread != NULL)
    {
        ASSERT_VALID(pThread);
        ASSERT(pThread != AfxGetApp());
        // cleanup OLE if required
        if (pThread->m_lpfnOleTermOrFreeLib != NULL)
            (*pThread->m_lpfnOleTermOrFreeLib)(TRUE, FALSE);
        if (bDelete)
            pThread->Delete();
        pState->m_pCurrentWinThread = NULL;
    }
    // allow cleanup of any thread local objects
    AfxTermThread();
    // allow C-runtime to cleanup, and exit the thread
    _endthreadex(nExitCode);
}

從MFC代碼中能夠看出,AfxEndThread函數經過調用_endthreadex函數終止線程。此外,函數還進行釋放線程的堆棧、刪除線程對象等工做。

若是在其它線程中終止該線程,必須採用線程通訊的方法實現。其中一種簡單的方法是創建一個變量,讓線程監視該變量,當該變量爲某個值時,則終止線程。

(1)建立1個基於對話框的應用程序,名稱爲Demo。

(2)在IDD_DEMO_DIALOG對話框資源中添加控件,如表所示。

類型 ID 標題
Static IDC_STATIC 數據:
Edit IDC_DATA  
Button IDC_BEGIN_THREAD 啓動線程
Button IDC_END_THREAD 終止線程

 

 

 

 

 

(3)在文件中定義線程傳遞參數的數據結構,代碼以下: 

// DemoDlg.h
typedef struct THREAD_PARAM
{
    HWND hWnd;
    int nData;
    BOOL bExit;
}_THREAD_PARAM;

(4)在CDemoDlg類中添加成員變量,代碼以下:

// DemoDlg.h
protected:
    CWinThread* m_pThread;
    THREAD_PARAM m_ThreadParam;

(5)在CDemoDlg類的構造函數中初始化成員變量,代碼以下:

// DemoDlg.cpp
CDemoDlg::CDemoDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDemoDlg::IDD, pParent)
{
    // ...
    m_pThread = NULL;
    m_ThreadParam.nData = 0;
}

(6)在CDemoDlg類的OnInitDialog函數中添加以下代碼:

// DemoDlg.cpp  
 BOOL CDemoDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    //
    SetDlgItemInt(IDC_DATA, m_nData);
    return TRUE;
}

(7)在文件中定義線程消息,代碼以下:

// DemoDlg.h
#define WM_THREADMSG WM_USER+1

(8)在文件中定義線程函數,代碼以下:

// DemoDlg.h
UINT ThreadProc(LPVOID pParam);
// DemoDlg.cpp
UINT ThreadProc(LPVOID pParam)
{
    //線程參數
    THREAD_PARAM* pThreadParam = (THREAD_PARAM*)pParam;
    while (!pThreadParam->bExit)
    {
        Sleep(100);
        pThreadParam->nData++;
        //向主線程窗口發送消息
        ::PostMessage(pThreadParam->hWnd, WM_THREADMSG, 0, 0);
    }
    return 0;
}

(9)在CDemoDlg類中分別爲Button控件添加BN_CLICKED添加消息處理函數,代碼以下:

// DemoDlg.cpp
void CDemoDlg::OnBeginThread()
{
    if (m_pThread != NULL)
    {
        AfxMessageBox(_T("線程已經啓動。"));
        return;
    }
    m_ThreadParam.hWnd = m_hWnd;
    m_ThreadParam.bExit = FALSE;
    
    //啓動線程,初始爲掛起狀態
    m_pThread = AfxBeginThread(ThreadProc, &m_ThreadParam,
    THREAD_PRIORITY_ABOVE_NORMAL, 0, CREATE_SUSPENDED);
    
    //線程結束時不自動刪除
    m_pThread->m_bAutoDelete = FALSE;
    
    //恢復線程運行
    m_pThread->ResumeThread();
}

void CDemoDlg::OnEndThread()
{
    if (m_pThread == NULL)
    {
        AfxMessageBox(_T("線程已經終止。"));
        return;
    }
    m_ThreadParam.bExit = TRUE;
    
    //等待線程結束
    ::WaitForSingleObject(m_pThread->m_hThread, INFINITE);
    delete m_pThread;
    m_pThread = NULL;
}

(10)在CDemoDlg類中添加自定義消息處理函數,代碼以下:

// DemoDlg.h
afx_msg LRESULT OnMsgFunc();

// DemoDlg.cpp
BEGIN_MESSAGE_MAP(CDemoDlg, CDialog)
    ON_MESSAGE(WM_THREADMSG, OnMsgFunc)
END_MESSAGE_MAP()

LRESULT CDemoDlg::OnMsgFunc()
{
    SetDlgItemInt(IDC_DATA, m_ThreadParam.nData);
    return 1;  
}
相關文章
相關標籤/搜索