您需要的執行人線程池和一些類:
一個Fsearch類。這包含您的容器的結果。它還有一個工廠方法,它返回一個Ffolder,計算一個'foldersOutstanding'計數器,以及一個通過倒計數'foldersOutstanding'來計算它們的OnComplete:
您需要一個Ffolder類來表示一個文件夾並被傳遞其路徑作爲ctor參數。它應該有一個迭代的運行方法是作爲參數提供的文件夾路徑以及Fsearch實例。
使用根文件夾創建並加載Fsearch並將其放入池中。它創建一個文件夾類,傳遞其根路徑和itslef,並加載它。然後它等待一個'searchComplete'事件。
第一個文件夾迭代其文件夾,爲每個'普通'文件創建DFiles,並將它們推送到Fsearch容器中。如果它找到一個文件夾,它將從Fsearch獲取另一個Ffolder,並將其加載到新的路徑中,並將其加載到池中。
當Ffolder完成迭代自己的文件夾時,它會調用Fsearch的OnComplete方法。 OnComplete正在對'foldersOutstanding'進行倒計時,當它減到零時,所有文件夾都被掃描並處理文件。執行此最終遞減的線程會標記searchComplete事件,以便Fsearch可以繼續。 Fsearch可以調用創建時傳遞的一些「OnSearchComplete」事件。
毫無疑問,Fsearch回調必須是線程安全的。
這樣的練習不一定要學術。 Fsearch中的所有DFiles所在的容器可以是生產者 - 消費者隊列。其他線程可以在搜索過程中開始處理DFiles,而不是等到結束。
我以前做過這個(但不是在Java中) - 它工作正常。像這樣的設計可以輕鬆地並行執行多個搜索 - 同時發佈多個硬盤驅動器根目錄的Fsearch非常有趣 - 咔嗒聲令人印象深刻
忘了說 - 這種設計的巨大收益是當搜索幾個網絡驅動器具有高延遲。他們都可以並行搜索。一個悲慘的單線程順序搜索加速很多次。當單線程完成一個驅動器的DFiles排隊時,多搜索搜索了四個驅動器,並且已經處理了大部分DFiles。
注:
1)如果如上述嚴格執行,線程池線程taht執行FSearch被阻止對「OnSearchComplete」事件直到搜索已經結束,因此「使用了」一個線程。因此,必須有比實時Fsearch實例多的線程池線程,否則將不會有線程留下來進行實際搜索(當然,這發生在我身上:))。
2)與單線程搜索不同,結果不會以任何可預測或可重複的順序返回。例如,如果您將結果標示爲GUI線程並嘗試在TreeView中顯示它們,則通過treeview組件的路徑對於每個結果可能會有所不同,更新可視化樹視圖將會很長。這可能會導致Windows GUI輸入隊列變滿(10000限制),因爲GUI無法跟上,或者如果使用Ffolder的對象池等,池可能會清空,性能下降,並且如果GUI線程嘗試獲取一個Ffolder從空池中發出一個新的搜索並阻止,所有Ffolder實例在Windows消息中出現全面死鎖,(當然,這發生在我身上:)。最好不要讓這種事情發生!
示例 - 類似這樣的事情我發現 - 這是非常舊的Windows/C++ Builder代碼,但它仍然有效 - 我在Rad Studio 2009上試用了它,刪除了所有舊版/專有gunge並添加了一些額外的評論。這裏所做的只是計算文件夾和文件,就像一個例子。只有幾個'runnable'類myPool-> submit()方法將一個runnable加載到池中,並且run()方法被執行。基本ctor有一個'OnComplete'EventHander,(TNotifyEvent),delgate參數 - 當run()方法返回時,由pool線程觸發。
// ******************************* CLASSES ************ ********************
class DirSearch; // forward dec.
class ScanDir:public PoolTask{
String FmyDirPath;
DirSearch *FmySearch;
TStringList *filesAndFolderNames;
public: // Counts for FmyDirPath only
int fileCount,folderCount;
ScanDir(String thisDirPath,DirSearch *mySearch);
void run(); // an override - called by pool thread
};
class DirSearch:public PoolTask{
TNotifyEvent FonComplete;
int dirCount;
TEvent *searchCompleteEvent;
CRITICAL_SECTION countLock;
public:
String FdirPath;
int totalFileCount,totalFolderCount; // Count totals for all ScanDir's
DirSearch(String dirPath, TNotifyEvent onComplete);
ScanDir* getScanDir(String path); // get a ScanDir and inc's count
void run(); // an override - called by pool thread
void __fastcall scanCompleted(TObject *Sender); // called by ScanDir's
};
// ********************* **********方法********************************
// ctor - just calls base ctor an initialzes stuff..
ScanDir::ScanDir(String thisDirPath,DirSearch *mySearch):FmySearch(mySearch),
FmyDirPath(thisDirPath),fileCount(0),folderCount(0),
PoolTask(0,mySearch->scanCompleted){};
void ScanDir::run() // an override - called by pool thread
{
// fileCount=0;
// folderCount=0;
filesAndFolderNames=listAllFoldersAndFiles(FmyDirPath); // gets files
for (int index = 0; index < filesAndFolderNames->Count; index++)
{ // for all files in the folder..
if((int)filesAndFolderNames->Objects[index]&faDirectory){
folderCount++; //do count and, if it's a folder, start another ScanDir
String newFolderPath=FmyDirPath+"\\"+filesAndFolderNames->Strings[index];
ScanDir* newScanDir=FmySearch->getScanDir(newFolderPath);
myPool->submit(newScanDir);
}
else fileCount++; // inc 'ordinary' file count
}
delete(filesAndFolderNames); // don't leak the TStringList of filenames
};
DirSearch::DirSearch(String dirPath, TNotifyEvent onComplete):FdirPath(dirPath),
FonComplete(onComplete),totalFileCount(0),totalFolderCount(0),dirCount(0),
PoolTask(0,onComplete)
{
InitializeCriticalSection(&countLock); // thread-safe count
searchCompleteEvent=new TEvent(NULL,false,false,"",false); // an event
// for DirSearch to wait on till all ScanDir's done
};
ScanDir* DirSearch::getScanDir(String path)
{ // up the dirCount while providing a new DirSearch
EnterCriticalSection(&countLock);
dirCount++;
LeaveCriticalSection(&countLock);
return new ScanDir(path,this);
};
void DirSearch::run() // called on pool thread
{
ScanDir *firstScanDir=getScanDir(FdirPath); // get first ScanDir for top
myPool->submit(firstScanDir); // folder and set it going
searchCompleteEvent->WaitFor(INFINITE); // wait for them all to finish
}
/* NOTE - this is a DirSearch method, but it's called by the pool threads
running the DirScans when they complete. The 'DirSearch' pool thread is stuck
on the searchCompleteEvent, waiting for all the DirScans to complete, at which
point the dirCount will be zero and the searchCompleteEvent signalled.
*/
void __fastcall DirSearch::scanCompleted(TObject *Sender){ // a DirSearch done
ScanDir* thiscan=(ScanDir*)Sender; // get the instance that completed back
EnterCriticalSection(&countLock); // thread-safe
totalFileCount+=thiscan->fileCount; // add DirSearch countst to totals
totalFolderCount+=thiscan->folderCount;
dirCount--; // another one gone..
LeaveCriticalSection(&countLock);
if(!dirCount) searchCompleteEvent->SetEvent(); // if all done, signal
delete(thiscan); // another one bites the dust..
};
..在這裏,工作:
這是錯誤? – SJuan76 2012-04-14 11:26:57
對此沒有正確的答案,程序員可能是一個更好的地方,但你最主要的問題是你似乎試圖在一個線程中做太多。如果是我,我會有一個具有路徑參數/屬性的任務隊列,而不是路徑/文件列表。 – 2012-04-14 11:32:53
@TonyHopkinson - 是的,這是如何做 - 排隊文件夾任務。 – 2012-04-14 12:44:54