您需要 Executor 线程池和一些类:
一个 Fsearch 类。这包含您的结果容器。它还有一个工厂方法,该方法返回一个 Ffolder,对“foldersOutstanding”计数器进行计数,以及一个 OnComplete,它通过对“foldersOutstanding”进行计数来重新计算它们:
您需要一个 Ffolder 类来表示一个文件夹,并将其路径作为 ctor 参数传递。它应该有一个迭代的运行方法是作为参数提供的文件夹路径以及 Fsearch 实例。
使用根文件夹创建并加载一个 Fsearch 并将其发射到池中。它创建一个文件夹类,传递它的根路径和它的文件,然后加载它。然后它等待“searchComplete”事件。
第一个 Ffolder 迭代其文件夹,为每个“普通”文件创建(或分离)DFiles,并将它们推送到 Fsearch 容器中。如果它找到一个文件夹,它会从 Fsearch 中获取另一个 Ffolder,用新路径加载它,并将其加载到池中。
当 Ffolder 完成对自己的文件夹的迭代时,它会调用 Fsearch 的 OnComplete' 方法。OnComplete 正在倒计时“foldersOutstanding”,当它减到零时,所有文件夹都已被扫描并已处理文件。执行此最终减量的线程向 searchComplete 事件发出信号,以便 Fsearch 可以继续。Fsearch 可以调用一些在创建时传递的“OnSearchComplete”事件。
毫无疑问,Fsearch 回调必须是线程安全的。
这样的练习不一定是学术性的。所有 DFile 所在的 Fsearch 中的容器可能是一个生产者-消费者队列。其他线程可以在搜索过程中开始处理 DFile,而不是等到结束。
我以前做过这个(但不是在 Java 中),它工作正常。像这样的设计可以轻松地并行执行多个搜索 - 一次发出多个硬盘驱动器根的 Fsearch 很有趣 - 咔哒声令人印象深刻
忘了说 - 这种设计的最大好处是在搜索多个具有高延迟的网络驱动器时。它们都可以并行搜索。单线程顺序搜索的速度提升了很多倍。当单线程搜索完成对一个驱动器的 DFile 的排队时,多重搜索已经搜索了四个驱动器并且已经处理了大部分 DFile。
笔记:
1) 如果按照上面严格执行,执行 FSearch 的线程池线程在 'OnSearchComplete' 事件上被阻塞,直到搜索结束,所以'用完'一个线程。因此,线程池线程必须比活动的 Fsearch 实例多,否则将没有剩余线程来进行实际搜索(是的,这当然发生在我身上:)。
2) 与单线程搜索不同,结果不会以任何可预测或可重复的顺序返回。例如,如果您在结果进入 GUI 线程时发出信号并尝试在 TreeView 中显示它们,则每个结果通过 treeview 组件的路径可能会有所不同,更新可视化 treeview 会很长。这可能会导致 Windows GUI 输入队列变满(10000 个限制),因为 GUI 无法跟上,或者,如果将对象池用于 Ffolder 等,池可能会清空,从而降低性能,并且如果 GUI 线程尝试获取一个 Ffolder 从空池中发出新的搜索等阻塞,所有 Ffolder 实例都卡在 Windows 消息中的全面死锁(是的,当然这发生在我身上:)。最好不要让这样的事情发生!
示例 - 我发现类似这样的东西 - 它是相当旧的 Windows/C++ Builder 代码,但它仍然有效 - 我在我的 Rad Studio 2009 上尝试过,删除了所有遗留/专有工具并添加了一些额外的注释。它在这里所做的只是计算文件夹和文件,就像一个例子一样。只有几个“可运行”类 myPool->submit() 方法将一个可运行对象加载到池中,然后它的 run() 方法被执行。基本 ctor 有一个 '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..
};
..在这里,工作: