从父线程终止工作线程 - MFC

时间:2014-01-14 14:59:35

标签: multithreading visual-c++ mfc worker-thread

我刚开始学习MFC,我正在编写一个基于对话框的应用程序,以便更好地理解多线程。

主对话框有一个进度条,一个开始按钮和一个取消按钮。

点击开始按钮,我正在创建一个工作线程进行一些处理(通过API调用),主线程负责进度条。

我已经定义了几个Windows消息来更新并停止进度条状态

WM_UPDATE_CONTROL
WM_STOP_CONTROL

以下是我到目前为止创建的代码

HWND* phObjectHandle;
CWinThread* thread;

void CprogCtrlDlg::OnBnClickedStart() {
    phObjectHandle = new HWND;    // Set object handle for Worker thread
    *phObjectHandle = GetSafeHwnd();

    // create worker thread
    if(NULL == (thread = AfxBeginThread(ThreadFunc, phObjectHandle))) {
        EndDialog(IDCANCEL);
    }

    AfxMessageBox(L"Thread started");
    // Set Progress bar to marquee
}

void CprogCtrlDlg::OnBnClickedCancel() {
    // kill the Worker thread
}

UINT CprogCtrlDlg::ThreadFunc(LPVOID pParam) { 
    HWND *pObjectHandle = static_cast<HWND *>(pParam);
    CprogCtrlImpDlg* threadDlg = (CprogCtrlImpDlg*) pParam;

    return threadDlg->ThreadFuncRun(pObjectHandle);
}

UINT CprogCtrlDlg::ThreadFuncRun(HWND* pObjectHandle) {

    ::PostMessage(*pObjectHandle, WM_UPDATE_CONTROL, 0, 0);

    // repetitive API CALL in a loop

    ::PostMessage(*pObjectHandle, WM_STOP_CONTROL, 0, 0);
    AfxMessageBox(L"Thread completed");

    return 0;
}

如果单击“取消”按钮,我想从Parent线程终止Worker线程。

我尝试使用TerminateThread()(尽管它不是建议的)但我无法杀死该线程。

请评论并分享您对从父线程终止工作线程的想法。

我在Windows 7上使用visual studio 2010

TIA

1 个答案:

答案 0 :(得分:5)

我会修改你的代码。

在对话框类中有一些成员变量来保存线程句柄和事件句柄(在构造函数中初始化为NULL):

CWinThread* m_hThread;
HANDLE m_hKillEvent;

使用静态函数作为线程入口点,将对话框this作为参数传递,然后将调用委托给类实例,以便访问所有对话框的变量:

UINT ThreadFunc(LPVOID pParam) 
{ 
    // static thread func - delegate to instance
    CprogCtrlDlg* pDlg = static_cast<CprogCtrlDlg*>(pParam);
    return pDlg->ThreadFuncRun();
}

启动线程时,也要创建一个事件:

void CprogCtrlDlg::OnBnClickedStart() 
{
    // create worker thread
    m_hKillEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    m_hThread = AfxBeginThread(ThreadFunc, this);
    AfxMessageBox(L"Thread started");
}

要杀死线程,只需设置事件并等待线程句柄,这将在死亡时发出信号:

void CprogCtrlDlg::OnBnClickedCancel() 
{
    // kill the Worker thread
    SetEvent(m_hKillEvent);

    // wait for it to die
    DWORD dwRet = WaitForSingleObject(m_hThread->m_hThread, 5000);
    if (dwRet == WAIT_TIMEOUT)
    {
        // thread failed to die after 5 seconds
        // error handling (maybe TerminateThread here)
    }
}

在线程函数(现在在对话框类中)中,您可以向自己发布消息以指示进度并使用等待事件来捕获终止请求:

UINT CprogCtrlDlg::ThreadFuncRun() 
{
    // instance thread func
    PostMessage(WM_UPDATE_CONTROL, 0, 0);

    // main loop
    while (true)
    {
        // check kill
        DWORD dwRet = WaitForSingleObject(m_hKillEvent, 0);
        if (dwRet == WAIT_OBJECT_0) break;

        // do a little work here and update progress
        // ... so this is part of your working loop ...
        PostMessage(WM_UPDATE_CONTROL, 0, 1 /*2,3,4,...*/);
    }

    // normal thread exit
    PostMessage(WM_STOP_CONTROL, 0, 0);
    return 0;
}

我遗漏了初始化,指针清理,手柄等等,但你得到了我希望的一般想法。

您可以通过多种方式对线程循环进行编码,您可以像上面那样定期检查事件是否已发出信号,或者您可以等待事件发出信号以完成工作。两者都是常见的模式,通常与两个事件一起使用 - 一个用于触发工作,另一个用于查杀。如果等待多个事件,请参阅this answer以了解要注意的一些要点。

对于简单的进度条更新,您可以将事件检查放在工作循环中,如下所示:

UINT CprogCtrlDlg::ThreadFuncRun() 
{
    // instance thread func
    PostMessage(WM_UPDATE_CONTROL, 0, 0);

    // main loop
    for (int i = 0; i < 100; ++i)
    {
        // check kill
        DWORD dwRet = WaitForSingleObject(m_hKillEvent, 0);
        if (dwRet == WAIT_OBJECT_0) break;

        // do a little work here and update progress
        PostMessage(WM_UPDATE_CONTROL, 0, (LPARAM)i);
    }

    // normal thread exit
    PostMessage(WM_STOP_CONTROL, 0, 0);
    return 0;
}