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; }