安全终止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;  
}
相关文章
相关标签/搜索