博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
安全终止MFC线程全
阅读量:4304 次
发布时间:2019-05-27

本文共 4579 字,大约阅读时间需要 15 分钟。

终止线程 

有两种情况可以使线程结束:控制函数结束或者根本就不允许线程完成,而提前终止它。我们可以想象在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);

其中:

nExitCode:线程的退出码。

MFC还提供了AfxEndThread函数,Worker线程和UI线程都可以通过在线程内部调用AfxEndThread函数结束线程。

AfxEndThread函数的定义如下:

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

其中:

nExitCode:线程的退出码。

在MFC的THRDCORE.CPP中,AfxEndThread函数的相关代码如下:

复制代码

// THRDCORE.CPPvoid 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.htypedef struct THREAD_PARAM{    HWND hWnd;    int nData;    BOOL bExit;}_THREAD_PARAM;

复制代码

(4)在CDemoDlg类中添加成员变量,代码如下:

// DemoDlg.hprotected:    CWinThread* m_pThread;    THREAD_PARAM m_ThreadParam;

(5)在CDemoDlg类的构造函数中初始化成员变量,代码如下:

复制代码

// DemoDlg.cppCDemoDlg::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.hUINT ThreadProc(LPVOID pParam);// DemoDlg.cppUINT 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.cppvoid 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.hafx_msg LRESULT OnMsgFunc();// DemoDlg.cppBEGIN_MESSAGE_MAP(CDemoDlg, CDialog)    ON_MESSAGE(WM_THREADMSG, OnMsgFunc)END_MESSAGE_MAP()LRESULT CDemoDlg::OnMsgFunc(){    SetDlgItemInt(IDC_DATA, m_ThreadParam.nData);    return 1;  }

复制代码

转载地址:http://pnmws.baihongyu.com/

你可能感兴趣的文章
How it works(12) Tileserver-GL源码阅读(A) 服务的初始化
查看>>
uni-app 全局变量的几种实现方式
查看>>
echarts 为例讲解 uni-app 如何引用 npm 第三方库
查看>>
uni-app跨页面、跨组件通讯
查看>>
springmvc-helloworld(idea)
查看>>
JDK下载(百度网盘)
查看>>
idea用得溜,代码才能码得快
查看>>
一篇掌握python魔法方法详解
查看>>
数据结构和算法5-非线性-树
查看>>
数据结构和算法6-非线性-图
查看>>
数据结构和算法7-搜索
查看>>
数据结构和算法8-排序
查看>>
windows缺少dll解决办法
查看>>
JPA多条件动态查询
查看>>
JPA自定义sql
查看>>
BigDecimal正确使用了吗?
查看>>
joplin笔记
查看>>
JNDI+springmvc使用
查看>>
vue+springboot分页交互
查看>>
vue+springboot打包发布
查看>>