实话实说,对于VC多线程编程我只是一知半解。虽然也做过很多多线程的应用,但也都是很简单的,没有什么过多的控制。还是有一次看一个帖子讨论类似杀毒软件的线程控制方式,当时我也参与了一下,而且为了挑战一下自己我按着自己的想法写了一个实现。既然东西都做出来了,想了想是不是可以把那个例子共享出来,和大家讨论一下,同时也是一个交流学习的过程。
对于杀软的工作流程我想大家都应该不陌生,简单说就是启动一个线程对指定路径进行扫描,中途可以暂停和恢复扫描工作,也可以直接停止扫描过程。对于那个工作线程来说那就是挂起和回复线程,同时可以结束线程。应该说除了线程同步以外这个过程包含了所有的线程的基本控制,当然这里面采用的方法也可以运用于线程同步中。
好了,下面结合我的例子说一下我的实现方法。首先出于编程方便的考虑我采用对话框来做这个demo,demo已上传到我的资源里,大家可以自行下载。程序界面很简单,一个开始扫描按钮,一个停止扫描按钮,一个richedit的信息输出窗口。当单击开始扫描按钮时会启动一个线程,同时此按钮变为暂停扫描按钮,无论是在线程运行还是在停止状态下单击停止扫描按钮都会将线程安全退出。通过输出窗口可以监视线程的工作状态,这些信息都是从线程中传递出来的。下图是一个工作中的界面截图:
创建线程的部分比较简单,是通过_beginthreadex函数实现的,值得一提的是为了和主界面做交互我将窗口句柄作为参数传给了线程。但是开始扫面按钮里的代码稍显复杂一些,因为这里涉及到开始、暂停和继续三种线程控制功能,不过总体上来看代码还是简单、易懂的。
[cpp] view plaincopy
- void CScanDemoDlg::OnClickButtonCtrl()
- {
- CString strBtnText;
- GetDlgItem(IDC_BUTTON_CTRL)->GetWindowText(strBtnText);
- if(strBtnText==_T("开始扫描"))
- {
- UINT uThreadID;
- SetEvent(m_hEvent);
- m_nCount = 100;
- m_hThread = (HANDLE)_beginthreadex(NULL, 0, &ThreadScan, GetSafeHwnd(), 0, &uThreadID);
- GetDlgItem(IDC_BUTTON_CTRL)->SetWindowText(_T("暂停扫描"));
- GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(TRUE);
- }else if(strBtnText==_T("暂停扫描"))
- {
- ResetEvent(m_hEvent);
- GetDlgItem(IDC_BUTTON_CTRL)->SetWindowText(_T("继续扫描"));
- }else if(strBtnText==_T("继续扫描"))
- {
- SetEvent(m_hEvent);
- GetDlgItem(IDC_BUTTON_CTRL)->SetWindowText(_T("暂停扫描"));
- }
- }
通过代码可以看到我实现线程控制是通过Event事件来做的,下面具体说说我实现线程控制的方法。在线程内部有一个计数器m_nCount,如果不强行结束线程那循环m_nCount次之后线程就会自动结束。每一次循环都通过WaitForSingleObject函数来判断m_hEvent的状态,如果是有信号状态就执行循环,如果是无信号状态就一直等待,当然线程也就不工作了,开始扫描和暂停扫描就是通过设置m_hEvent的状态来实现的。线程函数的具体代码如下:
[cpp] view plaincopy
- unsigned __stdcall CScanDemoDlg::ThreadScan(LPVOID hMainWnd)
- {
- HWND hWnd = (HWND)hMainWnd;
- TCHAR szMsg[255];
- while(m_nCount>0)
- {
- WaitForSingleObject(m_hEvent, INFINITE);
- Sleep(100);
- _tcscpy_s(szMsg, 255, _T("扫描中"));
- ::SendMessage(hWnd, WM_OUTPUTINFO, WPARAM(szMsg), NULL);
- m_nCount--;
- }
- _tcscpy_s(szMsg, 255, _T("扫描结束"));
- ::SendMessage(hWnd, WM_OUTPUTINFO, WPARAM(szMsg), NULL);
- return 1;
- }
可以看到线程函数还是很简单的,这里面m_hEvent和m_nCount都是CScanDemoDlg类的静态成员变量,这样它们才能在线程函数中正常使用。线程函数中还要说明的一点就是通过发消息的方式和主对话框通信,这也是线程间通信的常用方法。这里用SendMessage发消息的原因是承载消息内容的szMsg是线程的内部变量,SendMessage可以保证执行消息时线程函数不继续执行从而不会破坏szMsg的内容。WM_OUTPUTINFO是一个自定义消息,作用就是输出线程的工作状态信息。安全停止这个线程比较容易,保证m_hEvent有信号的前提下将m_nCount置0即可。具体代码如下:
[cpp] view plaincopy
- void CScanDemoDlg::OnClickButtonStop()
- {
- SetEvent(m_hEvent);
- m_nCount = 0;
- }
这个程序可以较好的实现预期的控制目的,而且方法比较简单实用。当然,实现相同控制功能的方法应该还有很多,线程控制、线程同步也是我们编程过程中经常遇到的问题,希望在这方面有丰富经验的朋友们等够分享一下自己在这个领域的经验和方法。