要点
描述在Windows/MFC开发环境下,对于Start、Pause、Stop等典型操作的示例代码。
UI
示例效果如下:
- Start:从0开始,计算每个整数的平方根,并显示在编辑框中;
- Pause:暂停计算;Pause之后,可以Continue
- Stop:停止计算。
要点
- 用一个线程执行(耗时)任务;
- ResumeThread()恢复线程的执行,相当于Continue;
- SuspendThread()把线程挂起,相当于Pause;
- 对于Stop,直接退出线程函数。
代码
VS2010环境。
资源文件
IDD_STARTPAUSESTOP_DIALOG DIALOGEX 0, 0, 293, 84
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "StartPauseStop"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
PUSHBUTTON "Start",IDC_START,34,19,68,18
PUSHBUTTON "Pause",IDC_PAUSE,116,19,68,18
PUSHBUTTON "Stop",IDC_STOP,198,19,68,18
EDITTEXT IDC_MSG,35,52,230,17,ES_AUTOHSCROLL | ES_READONLY
END
头文件
这里没有特意考虑private或public。
class CStartPauseStopDlg : public CDialogEx
{
//...
private:
HANDLE m_hThread;
BOOL m_bStopped;
DWORD m_dwCurrent;
CString m_sMsg;
public:
static DWORD WINAPI ThreadProc(LPVOID lpThreadParameter);
void Doit();
void CloseThread();
afx_msg void OnBnClickedStart();
afx_msg void OnBnClickedPause();
afx_msg void OnBnClickedStop();
afx_msg void OnDestroy();
};
实现文件
省略了部分自动生成的代码。
//...
#include <math.h> // sqrt()
//...
CStartPauseStopDlg::CStartPauseStopDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CStartPauseStopDlg::IDD, pParent)
, m_sMsg(_T(""))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CStartPauseStopDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_MSG, m_sMsg);
}
BEGIN_MESSAGE_MAP(CStartPauseStopDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_START, &CStartPauseStopDlg::OnBnClickedStart)
ON_BN_CLICKED(IDC_PAUSE, &CStartPauseStopDlg::OnBnClickedPause)
ON_BN_CLICKED(IDC_STOP, &CStartPauseStopDlg::OnBnClickedStop)
ON_WM_DESTROY()
END_MESSAGE_MAP()
BOOL CStartPauseStopDlg::OnInitDialog()
{
//...
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;
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::OnBnClickedPause()
{
GetDlgItem(IDC_START)->EnableWindow(TRUE);
GetDlgItem(IDC_START)->SetWindowText(_T("Continue"));
GetDlgItem(IDC_PAUSE)->EnableWindow(FALSE);
::SuspendThread(m_hThread);
}
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); // Let the thread can exit by itself
CloseThread();
m_dwCurrent = 0;
}
void CStartPauseStopDlg::OnDestroy()
{
CDialogEx::OnDestroy();
CloseThread();
}
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, 2000);
}
return 0;
}
void CStartPauseStopDlg::Doit()
{
double d = sqrt(m_dwCurrent * 1.0);
m_sMsg.Empty();
m_sMsg.Format("sqrt(%d) = %lf", m_dwCurrent, d);
m_dwCurrent++;
//UpdateData(FALSE); //Debug Assertion Failed!
this->SetDlgItemText(IDC_MSG, m_sMsg);
//::AfxMessageBox("Do something ...");
}
void CStartPauseStopDlg::CloseThread()
{
if (NULL == m_hThread) return;
m_bStopped = TRUE;
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread);
m_hThread = NULL;
}
后记
示例代码中并没有考虑下面这种情况:在没有Pause&Stop的情况下,后台任务(线程)执行完了,这个时候应该主动去更新Start&Pause&Stop的Enable状态。
——当然,本例中线程是执行一个永无止境的任务。现实中,往往不是这样的。
因此,需要在线程真正完成后台任务的时候,去更新UI或执行类似的任务。