Executorスレッドプールといくつかのクラスが必要です。
Fsearchクラス。これには、結果のコンテナが含まれています。また、Ffolderを返し、「foldersOutstanding」カウンターをカウントアップするファクトリメソッドと、「foldersOutstanding」をカウントダウンしてカウントバックするOnCompleteもあります。
フォルダを表すにはFfolderクラスが必要であり、そのパスがctorパラメータとして渡されます。Fsearchインスタンスとともにパラメーターとして提供されるフォルダーパスを反復するrunメソッドが必要です。
ルートフォルダを使用してFsearchを作成してロードし、プールに起動します。ルートパスとそのファイルを渡してフォルダクラスを作成し、それをロードします。次に、「searchComplete」イベントを待機します。
その最初のFfolderはそのフォルダーを反復処理し、「通常の」ファイルごとにDFileを作成(またはデプール)し、それらをFsearchコンテナーにプッシュします。フォルダが見つかると、Fsearchから別のFfolderを取得し、新しいパスをロードして、それをプールにもロードします。
Ffolderは、自身のフォルダーの反復処理を終了すると、FsearchのOnComplete'メソッドを呼び出します。OnCompleteは「foldersOutstanding」をカウントダウンしており、ゼロにデクリメントされると、すべてのフォルダーがスキャンされ、ファイルが処理されます。この最後のデクリメントを行ったスレッドは、Fsearchを続行できるように、searchCompleteイベントを通知します。Fsearchは、作成時に渡された「OnSearchComplete」イベントを呼び出すことができます。
言うまでもなく、Fsearchコールバックはスレッドセーフでなければなりません。
そのような演習は、学術的である必要はありません。すべてのDFileが配置されるFsearchのコンテナーは、生産者/消費者キューである可能性があります。他のスレッドは、検索が終了するまで待つのではなく、検索の進行中にDFileの処理を開始する可能性があります。
私は以前にこれを行ったことがありますが(Javaではありません)、問題なく動作します。このような設計では、複数の検索を並行して簡単に実行できます-一度に複数のハードドライブルートに対してFsearchを発行するのは楽しいです-ガタガタという音が印象的です
言うのを忘れた-そのような設計からの大きな利益は、待ち時間の長い複数のネットワークドライブを検索するときです。それらはすべて並行して検索できます。悲惨なシングルスレッドのシーケンシャル検索のスピードアップは何度もあります。シングルスレッド検索が1つのドライブのDFileのキューイングを終了するまでに、マルチ検索は4つのドライブを検索し、すでにほとんどのDFileが処理されています。
ノート:
1)上記のように厳密に実装されている場合、スレッドプールスレッドtahtはFSearchを実行し、検索が終了するまで「OnSearchComplete」イベントでブロックされるため、1つのスレッドを「使い果たします」。したがって、ライブFsearchインスタンスよりも多くのスレッドプールスレッドが存在する必要があります。そうでない場合、実際の検索を実行するためのスレッドは残りません(もちろん、私に起こったことです:)。
2)シングルスレッド検索とは異なり、結果は予測可能な順序または繰り返し可能な順序で返されません。たとえば、結果がGUIスレッドに入ったときに結果を通知し、それらをTreeViewに表示しようとすると、ツリービューコンポーネントを通るパスが結果ごとに異なる可能性があり、ビジュアルツリービューの更新に時間がかかります。これにより、Windows GUI入力キューがいっぱいになる可能性があります(10000制限)。これは、GUIが追いつかないか、Ffolderなどにオブジェクトプールを使用している場合、プールが空になり、パフォーマンスが低下する可能性があり、GUIスレッドが取得しようとすると空のプールから新しい検索を発行するためのFfolderなど、すべてのFfolderインスタンスがWindowsメッセージでスタックした場合のオールラウンドなデッドロック(はい、もちろん私に起こりました:)。そのようなことが起こらないようにするのが最善です!
例-私が見つけたこのようなもの-それはかなり古いWindows/C ++ Builderコードですが、それでも機能します-私はRad Studio 2009でそれを試し、すべてのレガシー/プロプライエタリガンジを削除し、いくつかのコメントを追加しました。ここで行うのは、例として、フォルダーとファイルをカウントアップすることだけです。'runnable'クラスは2つしかありません。myPool->submit()メソッドは、runnableをプールにロードし、そのrun()メソッドが実行されます。ベースコンストラクターには、'OnComplete' EventHander(TNotifyEvent)、delgateパラメーターがあります。これは、run()メソッドが戻るときにプールスレッドによって起動されます。
//******************************* クラス **************** ****************
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..
};
..そしてここにあります、動作しています:
