MFC多线程技术
mfc中有两类线程,分别称之为工作者线程和用户界面线程。二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。
工作者线程没笑消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。用户界面线程一般用于处理独立于其他线程之外的用户输入,响应用户及系统产生的事件和消息等。但对于win32的api编程而言,这两种编程是没有区别的,他们都只需要线程的启动地址即可启动线程来执行任务。
在mfc中,一般用全局函数afxbeginthread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。这两种函数的重载和原型分别说明如下:
(1)工作者线程
cwndthread *afxbeginthread(afx_threadproc pfnthreadproc, lpvoid pparam, uint npriority=thread_priority_normal, uint nstacksize = 0, dword dwcreateflags = 0, lpsecurity_attributes lpsecurityattrs = null);
(2)iu线程(用户界面线程)
cwndthread *afxbeginthread(cruntimeclass *pthreadclass, int npriority=thread_priority_normal, uint nstacksize = 0, dword dwcreateflags = 0, lpsecurity_attributes lpsecurityattrs = null);
afxbeginthread()创建线程的流程不论哪个afxbeginthread(),首先都是创建mfc线程对象,然后创建win32线程对象。
afxbeginthread创建线程的流程图
mfc线程技术剖析
mfc的核心类库中有一个名为cwinthread的类,这个类在mfc的底层机理中占举足轻重的地位。
mfc应用程序
线程状态用类_afx_thread_state描述,模块状态用类_afx_module_state描述,模块-线程状态用类_afx_module_thread_state描述。这些类从类cnotrackobject派生。进程状态用类_afx_base_module_state描述,从模块状态_afx_module_state派生。进程状态是一个可以独立执行的mfc应用程序的模块状态。还有其他状态如dll的模块状态等也从模块状态类_afx_module_state派生。
mfc状态类的层次
模块、线程、模块-线程状态的关系
多线程实践案例:(多线程文件查找器)
查找文件的时候,首先用findfirstfile函数,如果函数执行成功,返回句柄hfindfile来对应这个寻找操作,接下来可以利用这个句柄循环调用findnextfile函数继续查找其他文件,知道该函数返回失败(false)为止。最后还要调用findclose函数关闭hfindfile句柄。
hfindfile = ::findfirstfile(lpfilename,lpfinddata); if(hfindfile != invalid_handle_value) { do // 处理本次找到的文件 { }while(::findnextfile(lpfilename,lpfinddata)); ::findcolse(hfindfile); }
文件搜索器要在指定的目录及所有子层目录中查找文件,然后向用户显示出查找的结果。如果使用多线程的话,就意味着各线程要同时在不同目录中搜索文件。
这个程序最关键的地方是定义了一个动态的目录列表。
ctypedsimplelist<cdirectorynode *> m_listdir; struct cdirectorynode : public cnotrackobject { cdirectorynode* pnext; // ctypedsimplelist类模板要用次成员 char szdir[max_path]; // 要查找的目录 }
在线程执行查找文件任务的时候,如果找到的是目录就将它添加到列表中,若找到的是文件,就用自定义checkfile函数进行比较,判断是否符合查找条件,若符合就打印出来,显示给用户。线程在查找完一个目录以后,再从m_listdir列表中取出一个新的目录进行查找,同时将该目录对应的结点从表中删除。
当m_listdir为空时,线程就要进入暂停状态,等待其他线程向m_listdir中添加新的目录。
案例:
rapidfile.h文件
#pragma once #include <afxwin.h> struct cdirectorynode : public cnotrackobject // 创建文件夹目录结构体 { cdirectorynode *pnext; // 文件夹目录的下一个指针 char szdir[max_path]; // 文件夹名称 } class crapidfinder { public: crapidfinder(int nmaxthread); // 构造函数 virtual ~crapidfinder(); // 虚析构函数 bool checkfile(lpctstr lpszfilename); // 匹配文件夹名字 int m_nresultcount; // 结果的数量 int m_nthreadcount; // 活动线程的数量 ctypesimplelist<cdirectorynode *> m_listdir; // 文件夹列表 critical_section m_cs; // 临界区 const int m_nmaxthread; // 最大线程数量 char m_szmatchname[max_path]; // 最大搜索的文件 // 通知线程的工作状态 handle m_hdirevent; //我们向m_listdir添加新的目录,10个线程 9个停止,1个工作 若m_listdir为空,线程不能停止 handle m_hexitevent; // 各个搜索线程是否已经结束 }
rapidfile.cpp文件
#include "rapidfile" #include <string> crapidfinder::crapidfinder(int nmaxthread) : m_nmaxthread(nmaxthread) { m_nresultcount = 0; m_nthreadcount = 0; m_szmatchname[0] = '\0'; m_listdir.construct(offsetof(cdirectorynode,pnext)); // 创建ctypedsimplelist m_hdirevent = ::createevent(null,false,false,null); m_hexitevent = ::createevent(null,false,false,null); ::initializecriticalsectioin(&m_cs); } crapidfinder::~crapidfinder() { ::closehandle(m_hdirevent); ::closehandle(m_hexitevent); ::deletecriticalsection(&m_cs); } // 查找文件名 bool crapidfinder::checkfile(lpctstr lpszfilename) { char str[max_path]; char strsearch[max_path]; strcpy(str,lpszfilename); strcpy(strsearch,m_szmatchname); _strupr(str); // 将字符串全部转换为大写 _strupr(strsearch); if(strstr(str,strsearch) != null) // 查找的文件名在里面 { return true; } return false; }
multithreadfindfile.cpp
#include <stdio.h> #include <afxwin.h> #include "rapidfile.h" uint finderentry(lpvoid lpparam) { crapidfinder *pfinder = (crapidfinder *)lpparam; cdirectorynode *pnode = null; // m_listdir从pnode中获取 bool bactive = true; // 线程状态 // 只要m_listdir有目录 while(1) { // 取出新目录 互斥的取待查目录 ::entercriticalsection(&pfinder->m_cs); if(pfinder->m_listdir.isempty()) bactive = false; else { pnode = pfinder->m_listdir.gethead(); pfinder->m_listdir.remove(pnode); } ::leavecriticalsection(&pfinder->m_cs); // bactive指示了当前线程的工作状态,如果m_listdir队列当前为空,那么我们当前线程先等待 if(!bactive) { ::entercriticalsection(&pfinder->m_cs); pfinder->m_nthreadcount--; if(pfinder->m_nthreadcount == 0) { ::leavecriticalsection(&pfinder->m_cs); break; } ::leavecriticalsection(&pfinder->m_cs); // 进入等待状态 resetevent(pfinder>m_hdirevent); ::waitforsingleobject(pfinder->m_hdirevent,infinite); ::entercriticalsection(&pfinder->m_cs); // 此时当前线程再度获得cpu的推进机会 pfinder->m_nthreadcount++; // 当前的活动线程数量加1 ::leavecriticalsection(&pfinder->m_cs); bactive = true; continue; } // 实现基于pnode的目录查找 win32_find_data filedata; handle hfindfile; if(pnode->szdir[strlen(pnode->szdir)-1] != '\') strcat(pnode->szdir,'\\'); strcat(pnode->szdir,"*.*"); hfindfile = ::findfirstfile(pnode->szdir,&filedata); if(hfindfile != invalid_handle_value) { do { if(filedata.cfilename[0] == '.') continue; if(filedata.dwfileattributes & file_attribute_directory) { // 是目录,添加到m_listdir cdirectorynode *p = new cdirectorynode; strncpy(p->szdir,pnode->szdir,strlen(pnode->szdir)-3); strcat(p->szdir,filedata.cfilename); ::entercriticalsection(&pfinder->m_cs); pfinder->m_listdir.addhead(p); ::leavecriticalsection(&pfinder->m_cs); // 置信一个事件 ::setevent(pfinder->m_hdirevent); } else // 文件 { if(pfinder->checkfile(filedata.cfilename)) // 找到文件名 { ::entercriticalsection(&pfinder->m_cs); ::interlockedincrement((long *)&pfinder->m_nresultcount); ::leavecriticalsection(&pfinder->m_cs); printf("%s \n",filedata.cfilename); } } }while(::findnextfile(pnode->szdir,&filedata)); } // 此节点的保存的目录已经全部搜索完毕 delete pnode; pnode = null; } ::setevent(pfinder->m_hexitevent); // 判断当前线程是否是最后一个结束循环的线程 if(::waitforsingleobject(pfinder->m_hdirevent,0) != wait_timeout) { // 通知主线程,最后一个搜索线程已经结束了 ::setevent(pfinder->m_hexitevent); } return 0; } int main(void) { crapidfinder *pfinder = new crapidfinder(64); // 开64个线程 cdirectorynode *pnode = new cdirectorynode; // 创建结点 char szpath[] = "c:\\"; // 需要查找的目录 char szfile[] = "stdafx"; // 需要查找的字符串 // 对crapider的信息进行设置 strcpy(pnode->szdir,szpath); // 设置要搜索的目录 pfinder->m_listdir.addhead(pnode); // 将要搜索的目录添加到list中,当做头结点 strcpy(pfinder->m_szmatchname,szfile); // 需要搜索的文件名 // 创建辅助线程 pfinder->m_nthreadcount = pfinder->m_nmaxthread; // 创建辅助线程,并等待查找结束 for(int i =0; i < pfinder->m_nmaxthread; i++) { afxbeginthread(finderentry,pfinder); } waitforsingleobject(pfinder->m_hexitevent,infinite); // 打印查找结果 printf("一共找到同名文件%d个\n"); delete pfinder; system("pause"); return 0; }
上一篇: block本质探寻六之修改变量