本程序用多線程的思想實現了一個文件查找類CRapidFinder,以加速文件的查找,運行效果圖如下:
其中成員函數:
//設置程序主窗口句柄,要匹配的文件名,路徑名
void FinderSet(HWND MainHwnd,CString MatchName="*.*",CString MatchDir="");
//設置線程數及優先級
void ThreadSet(LONG MaxThreadCount=5,int priority=0);
//搜索選項
void FinderOption(FINDEROPTION FinderOption);
//查找操作
BOOL StartFinder(); //開始查找
void PauseFinder(); //暫停查找
void ResumeFinder(); //繼續查找
void StopFinder(); //停止查找
void FinderReset(); //查找重置
//搜索選項
void FinderOption(FINDEROPTION FinderOption);
每個線程通過自定義的消息與UI線程通信,反映當前的查找進度與查找結果。
//自定義通知消息==========================================================
//WM_THREADEXIT 主線程結束 WPARAM: 結束碼
//WM_THREADCOUNT 活動線程數目 WPARAM: 線程數
//WM_FINDERITEM 查找結果 WPARAM: 結果字符串地址 LPARAM:文件屬性
//WM_THREADPAUSE 程序暫停
//WM_FINDERFOLDER當前查找目錄 WPARAM: 目錄字符串地址
//==================================================================
可通過ThreadSet()設置線程的數目與優先級,這樣MainThreadProc()中就會產生相應數目的線程
編程思路:
定義一個臨時目錄列表CStringList m_DirList; 線程每找到一個目錄就將它放在m_DirList尾,若找到的是文件,就用MatchProc()進行比較,判斷是否符合查找條件。若符合就向UI線程發送消息WM_FINDERITEM,以進行界面顯示。然後,線程繼續在當前目錄中查找直到當前目錄全部查找完畢。再從m_DirList隊頭取一個新的目錄進行查找。
這裡有一個要注意的地方就是m_DirList為每個線程所共有的資源,所以在訪問m_DirList時要注意一下線程間的互斥,這可以用臨界區操作來保證。
那麼,如何來判斷文件查找應該結束了呢?僅判斷m_DirList為空是不夠的,因為當m_DirList為空時,有可能還有活動的線程,這些活動的線程可能還會產生新的未查找的目錄,故只有在m_DirList為空且當前的活動線程數為0時才可以斷定查找結束。(這裡的"活動線程"指正在進行查找操作的線程,"非活動線程"指處於等待新的待查目錄狀態的線程)
這部分在線程函數中實現:
DWORD WINAPI CRapidFinder::ThreadProc(LPVOID lpParam)
{
CRapidFinder *finder=(CRapidFinder *)lpParam;
CFileFind filefinder;
CStringList filelist;
CString PathStr;
CString CurPath;
int re;
BYTE bNewActive=1,bOldActive;
CString *lpFolder=new CString;
while(1)
{
bOldActive=bNewActive;
if(WaitForSingleObject(finder->m_hExitEvent,0)!=WAIT_TIMEOUT)
{
if(bOldActive)InterlockedDecrement(&finder->m_ActiveCount);
PostMessage(finder->m_MainhWnd,WM_THREADCOUNT,(WPARAM)finder->m_ActiveCount,NULL);
break;
}
if(!finder->m_ActiveCount)
{
SetEvent(finder->m_hExitEvent);
finder->m_ExitCode=finder->EXIT;
break;
}
//進入臨界區
EnterCriticalSection(&finder->m_gCriticalSection);
if(finder->m_DirList.IsEmpty())bNewActive=0;
else
{
bNewActive=1;
*lpFolder=finder->m_DirList.RemoveHead();
CurPath=*lpFolder+_T("\\*.*");
}
LeaveCriticalSection(&finder->m_gCriticalSection);
//離開臨界區
if(bNewActive!=bOldActive)
{
bNewActive?InterlockedIncrement(&finder->m_ActiveCount):InterlockedDecrement(&finder->m_ActiveCount);
PostMessage(finder->m_MainhWnd,WM_THREADCOUNT,(WPARAM)finder->m_ActiveCount,NULL);
}
else if(!bNewActive) continue;
SendMessage(finder->m_MainhWnd,WM_FINDERFOLDER,(WPARAM)lpFolder,NULL);
if(filefinder.FindFile(LPCTSTR(CurPath)))
{
do
{
re=filefinder.FindNextFile();
if(filefinder.IsDots())continue;
PathStr=filefinder.GetFilePath();
if(filefinder.IsDirectory())
{
EnterCriticalSection(&finder->m_gCriticalSection);
finder->m_DirList.AddTail(PathStr);
LeaveCriticalSection(&finder->m_gCriticalSection);
}
else filelist.AddTail(PathStr);
}while(re);
}
while(!filelist.IsEmpty())
{
PathStr=filelist.RemoveHead();
if(finder->MatchProc(PathStr))
SendMessage(finder->m_MainhWnd,WM_FINDERITEM,(WPARAM)&PathStr,NULL);
}
}
delete lpFolder;
filefinder.Close();
return 0;
}
程序編譯運行後,僅進行文件名查找時經測試速度比Windows自帶的查找程序稍快(Win9x的查找程序是獨立的,而Win2000下是集成在Explorer中的),當線程數在5-10之間時速度最快,而超過50時速度反而變慢,這就說明了並不是線程越多越好。線程數過多,CPU的大部分開銷花在線程間切換上,而且由於沒有足夠的待查目錄,大多數的線程處在等待狀態,占用了大量的系統資源,從而使速度變慢。
當進行包含文字的查找時,我用的是KMP匹配算法,但是速度還是比Windows自帶查找程序慢,不知它的原理是什麼,還希望知道的朋友告知!
本例只是多線程應用的一個極簡單的例子,未免贻笑大方,筆者接觸多線程編程時間不長,其中的不妥之處,還請大家指正,歡迎與我聯系:[email protected]
程序在Win2000,VC++6.0通過。
本文配套源碼