MFC环境下Start&Pause&Stop操作(续):尽快结束任务

23 篇文章 0 订阅

在《MFC环境下Start&Pause&Stop操作》一文中,执行背景任务的线程中有下面一段话:

::WaitForSingleObject(hSleepEvent, 2000);

即线程周期性执行任务中间,会有一个固定的时间间隔。这种情况也是常见的,比如工程中周期性对数据采样的时候。时间有长有短,短的时候,用上面的这种WaitFor是可行的;长的时候,尽管功能不会有什么问题,但用户体验会下降。

为了说明问题,把上面的2s改成20s:

::WaitForSingleObject(hSleepEvent, 20000);

然后试着在Start之后单击Stop,Stop之后立即拖动这个UI的标题栏、或者鼠标单击下面的编辑框,则会较大概率出现下面的无响应:(直接关闭窗口看不出该效果)

no response

为方便说明问题原因,代码再贴一遍:

DWORD WINAPI CStartPauseStopDlg::ThreadProc(LPVOID lpThreadParameter)
{
    CStartPauseStopDlg* pObj = (CStartPauseStopDlg*)lpThreadParameter;

    HANDLE hSleepEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    for (;;) {
        if (pObj->m_bStopped) {
            //::AfxMessageBox("User stopped the task.");
            break;
        }   

        pObj->Doit();

        ::WaitForSingleObject(hSleepEvent, 20000);
    }

    return 0;
}

void CStartPauseStopDlg::OnBnClickedStop()
{
    m_bStopped = TRUE;
    GetDlgItem(IDC_START)->EnableWindow(TRUE);
    GetDlgItem(IDC_START)->SetWindowText(_T("Start"));
    GetDlgItem(IDC_PAUSE)->EnableWindow(FALSE);
    GetDlgItem(IDC_STOP)->EnableWindow(FALSE);

    ::ResumeThread(m_hThread);
    CloseThread();
    m_dwCurrent = 0;
}

void CStartPauseStopDlg::CloseThread()
{
    if (NULL == m_hThread) return;

    m_bStopped = TRUE;
    WaitForSingleObject(m_hThread, INFINITE);
    CloseHandle(m_hThread);
    m_hThread = NULL;
}

单击了Stop按钮之后,会执行OnBnClickedStop()函数,这个函数会唤醒执行线程,然后通过布尔变量m_bStopped让线程自己结束自己。而CloseThread()的WaitFor负责等待。线程真正结束之后,OnBnClickedStop()函数才返回。在这个过程中,拖动UI的标题栏,就会出现无响应。

如果唤醒线程后,线程刚好从for循环的下一句执行,立即判断出现要退出循环,则不会无响应。如果刚好碰到线程恢复后要执行周期性等待的WaitFor(类似于Sleep),则就无响应了。

如前面所述,如果这个等待时间过长,那么用户体验下降。

下面是一种解决方法,利用WaitForMultipleObject,即在原来Event的基础上,再增加一个Stop对应的Event。当用户选择Stop的时候,给该Event设置信号,WaitForMultipleObject可以立即退出。

示例代码:
在头文件中新增一个事件句柄:

HANDLE m_hStopEvent;

实现文件的部分代码(关注m_hStopEvent的部分):

BOOL CStartPauseStopDlg::OnInitDialog()
{
    ...

    // TODO: Add extra initialization here
    m_hStopEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    m_hThread = NULL;
    m_bStopped = FALSE;
    m_dwCurrent = 0;
    GetDlgItem(IDC_PAUSE)->EnableWindow(FALSE);
    GetDlgItem(IDC_STOP)->EnableWindow(FALSE);

    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CStartPauseStopDlg::OnBnClickedStart()
{
    m_bStopped = FALSE;
    ::ResetEvent(m_hStopEvent);
    if (NULL == m_hThread) {
        m_hThread = ::CreateThread(NULL, 0, ThreadProc, this, 0, NULL);
    } else {
        ::ResumeThread(m_hThread);
    }

    GetDlgItem(IDC_START)->EnableWindow(FALSE);
    GetDlgItem(IDC_PAUSE)->EnableWindow(TRUE);
    GetDlgItem(IDC_STOP)->EnableWindow(TRUE);
}

void CStartPauseStopDlg::OnBnClickedStop()
{
    m_bStopped = TRUE;
    GetDlgItem(IDC_START)->EnableWindow(TRUE);
    GetDlgItem(IDC_START)->SetWindowText(_T("Start"));
    GetDlgItem(IDC_PAUSE)->EnableWindow(FALSE);
    GetDlgItem(IDC_STOP)->EnableWindow(FALSE);
    ::SetEvent(m_hStopEvent);
    ::ResumeThread(m_hThread);
    CloseThread();
    m_dwCurrent = 0;
}

void CStartPauseStopDlg::OnDestroy()
{
    CDialogEx::OnDestroy();

    CloseThread();
    ::CloseHandle(m_hStopEvent);
}

DWORD WINAPI CStartPauseStopDlg::ThreadProc(LPVOID lpThreadParameter)
{
    CStartPauseStopDlg* pObj = (CStartPauseStopDlg*)lpThreadParameter;

    HANDLE hSleepEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    HANDLE hEvents[2] = {hSleepEvent, pObj->m_hStopEvent};
    for (;;) {
        if (pObj->m_bStopped) {
            //::AfxMessageBox("User stopped the task.");
            break;
        }   

        pObj->Doit();

        ::WaitForMultipleObjects(2, hEvents, FALSE, 20000);
    }

    return 0;
}

void CStartPauseStopDlg::CloseThread()
{
    if (NULL == m_hThread) return;

    m_bStopped = TRUE;
    ::SetEvent(m_hStopEvent);
    WaitForSingleObject(m_hThread, INFINITE);
    CloseHandle(m_hThread);
    m_hThread = NULL;
}

后记:

以上代码没有CloseHandle(m_hStopEvent);找个地方安放它吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值