傳統的檔案搜尋採用單執行緒遞迴遍歷目錄樹的方式,不僅程式碼複雜,且在目錄層級較深時效率極低。本文介紹如何利用多執行緒技術實現並行檔案搜尋,大幅提升搜尋效能。
採用「發現一個目錄即建立一個執行緒」的策略,實現同步並行搜尋:
HANDLE FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData);
BOOL FindNextFile(HANDLE hFindFile, LPWIN32_FIND_DATA lpFindFileData);
typedef struct tagTHREADPARAM {
CString strPath; // 搜尋路徑
CString strFileName; // 目標檔案名
} THREADPARAM, *LPTHREADPARAM;
// 同步物件
CMutex m_mutexThreadCount; // 執行緒計數器保護
CMutex m_mutexThreadParam; // 參數結構保護
CMutex m_mutexPath; // 結果陣列保護
CEvent m_event; // 完成通知事件
UINT uThreadCount = 0; // 活躍執行緒數
CStringArray m_strPathArray; // 搜尋結果儲存
UINT GetFilePathThreadProc(LPVOID pParam)
{
if (!pParam) AfxEndThread(0);
THREADPARAM* pThreadParam = (THREADPARAM*)pParam;
CString strPath = pThreadParam->strPath;
CString strFileName = pThreadParam->strFileName;
// 增加執行緒計數
m_mutexThreadCount.Lock();
uThreadCount++;
m_mutexThreadCount.Unlock();
WIN32_FIND_DATA* pInfo = new WIN32_FIND_DATA;
HANDLE hFile = ::FindFirstFile(strPath + _T("\\*.*"), pInfo);
if (hFile == INVALID_HANDLE_VALUE) {
delete pInfo;
// 減少計數並檢查是否全部完成
m_mutexThreadCount.Lock();
uThreadCount--;
if (uThreadCount == 0) m_event.SetEvent();
m_mutexThreadCount.Unlock();
return 0;
}
do {
// 跳過 "." 和 ".." 目錄
if (pInfo->cFileName[0] == '.') continue;
CString strFile = pInfo->cFileName;
if (pInfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
// 目錄:建立新執行緒遞迴搜尋
m_mutexThreadParam.Lock();
m_param.strPath = strPath + _T("\\") + strFile;
m_param.strFileName = strFileName;
AfxBeginThread(GetFilePathThreadProc, &m_param, THREAD_PRIORITY_NORMAL);
m_mutexThreadParam.Unlock();
} else {
// 檔案:比對檔案名
if (strFile == strFileName) {
m_mutexPath.Lock();
m_strPathArray.Add(strPath + _T("\\") + strFile);
m_mutexPath.Unlock();
}
}
} while (::FindNextFile(hFile, pInfo));
::FindClose(hFile);
delete pInfo;
// 執行緒結束,減少計數
m_mutexThreadCount.Lock();
uThreadCount--;
if (uThreadCount == 0) m_event.SetEvent();
m_mutexThreadCount.Unlock();
return 0;
}
void GetFilePath()
{
m_event.ResetEvent();
uThreadCount = 0;
m_strPathArray.RemoveAll();
m_param.strPath = m_strPath;
m_param.strFileName = m_strFileName;
// 啟動初始執行緒
AfxBeginThread(GetFilePathThreadProc, &m_param, THREAD_PRIORITY_NORMAL);
// 等待所有執行緒完成
::WaitForSingleObject(m_event.m_hObject, INFINITE);
if (m_strPathArray.GetUpperBound() == -1) {
AfxMessageBox(_T("未找到檔案"));
return;
}
for (int i = 0; i < m_strPathArray.GetSize(); i++) {
AfxMessageBox(m_strPathArray.GetAt(i));
}
}
維護執行緒物件陣列,需要終止時呼叫:
BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode);
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
