线程 / Thread
Document Infomation | File ID | : | 0805 | Create | : | 08-06-08 AM | Author | : | Joephia | Update | : | 08-06-08 AM | Abstract
如何使用线程 / How to use Thread [ 示例代码 ] [ Top ] 在介绍如何使用线程前,我们先简单了解线程的概念. 在一个应用程序中,首先拥有一个主线程,当创建了更多线程的时候,那些线程被称作子线程。而所有线程加在一起,并加上运行时所分配的内存,堆栈等就构成了进程。一个进程拥有唯一的标志符,即 PID (Process ID).作为一个线程,系统可以为其分配独立的CPU时间,但前提是要求主线程必须有空闲时间。所以如果在主线程没有空闲的时候,就无法继续消息循环,子线程是没有办法工作的。但是相反的,如果子线程没有空闲时间,那是不影响主线程的。 明白了线程关系后我们就知道应该把那些费时的工作放到子线程里实现,而主线程则处理用户交互和显示运行结果。例如在本例中需要实现的搜索文件功能。如果把搜索工作放到主线程里,那么程序就会在暂时性的进入无法响应的状态,有人建议用OnTimer函数,这是一种解决的方法,但决不是一种好的解决方法,因为不管把触发频率设的多高,其速度和线程相比相差的都不是一个数量级。 下面介绍下关于线程的几个API函数,仅介绍和本话题相关的功能。 ::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,(void*)this,0,NULL);
::SuspendThread(hThread);
::ResumeThread(hThread);
::WaitForSingleObject(hThread,1000);
::CloseHandle(hThread); ::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,(void*)this,0,NULL); 这个函数能创建线程,并返回该线程的句柄 hThread,值得注意的是 参数ThreadFunc是即将调用的函数入口地址,该函数需要冠以 CALLBACK 属性,并属于全局函数,在本例中使用 static CALLBACK 修饰。(void ×)this 这是个亮点,利用这个指针,我们可以调用静态函数不能调用的成员变量和函数,并省去了专门写数据包结构的麻烦事; ::SuspendThread(hThread); 这个函数可以将一个线程挂起,即暂时不分配CPU时间,那么该线程就会暂时停止执行 ::ResumeThread(hThread); 这个函数复杂把一个挂起的线程,重新激活,使其继续运行 ::WaitForSingleObject(hThread,1000); 这个函数等待一个线程的结束 ::CloseHandle(hThread); 关闭句柄的万能函数,在关闭一个线程句柄前,应该配合WaitForSingleObject,否则线程没有结束,而其资源却被释放了,就会导致破坏性的错误 继续回到搜索文件的话题,启动了一个线程我们就应该让他做点什么,比如显示搜索状态,在主界面显示当前搜索目录,当前搜索文件,以及一共找到了几个文件。在这里我们使用一个对话框程序CThreadDemoDlg作为主界面,那么给其添加几个CStatic控件可以显示结果。另外为了实现对线程的控制,我们添加了一个开关 m_bSwitchA ,当打开开关那么线程就会结束返回。下面是部分代码 //CThreadDemoDlg.h
//类声明文件中添加成员变量,为了使类的变量看起来整齐,结构清晰
//跟线程相关的参数全部放在了私有变量 arg 结构体中
//这是本例中的另一个亮点
private:
struct ArgList{
CString sCurFilename; //线程B中的当前文件名
DWORD nFinished; //线程B中的完成度
BOOL bSwitchA; //线程A中的结束开关
BOOL bSwitchB; //线程B中的结束开关
HANDLE hFindFile; //线程A句柄
HANDLE hRead; //线程B句柄
}arg; //CThreadDemoDlg.cpp
//启动线程,根据线程句柄判断是否已经有了线程
//如果有则恢复线程
if(arg.hFindFile)
::ResumeThread(arg.hFindFile);
else
arg.hFindFile=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadStart,(void*)this,0,NULL); //CThreadDemoDlg.cpp
//线程函数
void CThreadDemoDlg::ThreadStart(void *pView)
{
//搜索线程,由arg.bSwichA做为结束标志
//按照层遍历方式,遍历C:
CThreadDemoDlg *p=(CThreadDemoDlg *)pView;
CStringArray pathArray;
pathArray.Add(_T("C://"));
p->arg.bSwitchA=FALSE;
DWORD nTotal=0;
do{
CFileFind ff;
CString sFind=pathArray.GetAt(0);
p->m_directory.SetWindowText(pathArray.GetAt(0));
sFind+=L"//*.*";
sFind.Replace(_T(""),_T("//"));
BOOL bFind=ff.FindFile(sFind);
CString filename;
while(bFind){
if(p->arg.bSwitchA) //检测开关
return;
bFind=ff.FindNextFile();
if(ff.IsDots())
continue;
if(ff.IsDirectory())
{
pathArray.Add(ff.GetFilePath());
continue;
}
filename=ff.GetFileName();
p->m_file.SetWindowText(filename);
nTotal++;
CString tmp;
tmp.Format("%8u",nTotal);
p->m_total.SetWindowText(tmp);
}
pathArray.RemoveAt(0);
}while(pathArray.GetSize());
}
写到这里我们就可以实现文件搜索功能了。在下个话题,我将介绍如何使用进度对话框显示结果,敬请期待. |