4

我刚开始学习 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;
}

如果单击取消按钮,我想从父线程终止工作线程。

我尝试使用 TerminateThread()(虽然它不是建议的),但我无法杀死线程。

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

我在 Windows 7 上使用 Visual Studio 2010

TIA

4

1 回答 1

6

我会像这样修改你的代码。

在对话框类中有一些成员变量来保存线程句柄和事件句柄(在构造函数中初始化为 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;
}

我省略了初始化、指针清理、句柄等,但你得到了我希望的总体思路。

有几种方法可以编写线程循环,您可以像上面那样定期检查事件是否已发出信号,或者您可以等待事件发出信号以完成工作。两者都是常见的模式,通常与两个事件一起使用——一个用于触发工作,另一个用于杀死。如果等待多个事件,请参阅此答案以了解需要注意的一些要点。

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

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;
}
于 2014-01-14T15:19:40.333 回答