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