假设我必须在后台递归迭代存储在树结构中的项目,并且我想使用线程池中的多个线程(每个“文件夹”节点一个线程)遍历这棵树。我已经设法使用 OmniThreadLibrary 提供的几种不同的低级和高级方法来实现这一点。
但是,我还没有弄清楚如何正确地检测到扫描实际上已经完成,即每个最后的叶节点都已被处理。
我在网上找到了各种例子来检查是否GlobalThreadPool.CountExecuting + GlobalThreadPool.CountQueued <= 0
使用了IOmniTaskGroup.WaitForAll()
. 不幸的是,这些方法似乎都不适合我。检查总是返回True
得太早,即当仍有一些任务在运行时。我看过的所有例子都没有使用递归——以及那些没有使用线程池的例子——这可能不是一个好的组合吗?
这是我目前正在尝试执行此操作的(非常)简化的示例代码片段:
procedure CreateScanFolderTask(const AFolder: IFolder);
begin
CreateTask(ScanFolder)
.SetParameter('Folder', AFolder)
.Schedule();
end;
procedure ScanFolder(const ATask: IOmniTask);
var
lFolder,
lCurrentFolder: IFolder;
begin
if ATaks.CancellationToken.IsSignalled then Exit;
lCurrentFolder := ATask.Param['Folder'].AsInterface as IFolder;
DoSomethingWithItemsInFolder(lCurrentFolder.Items);
for lFolder in lCurrentFolder.Folders do
begin
if ATaks.CancellationToken.IsSignalled then Exit;
CreateScanFolderTask(lFolder);
end;
end;
begin
GlobalOmniThreadPool.MaxExecuting := 8;
CreateScanFolderTask(FRootFolder);
// ??? wait for recursive scan to finish
OutputResult();
end.
我尝试过的等待的一个示例实现是这样的(基于在 About.com 上找到的示例):
while GlobalOmniThreadPool.CountExecuting + GlobalOmniThreadPool.CountQueued > 0 do
Application.ProcessMessages;
但这似乎总是在“根线程”完成后立即退出。即使我使用Sleep()
-calls 添加人为延迟,它仍然总是过早退出。似乎在从执行任务列表中删除的一项任务与在该任务中安排的要添加到排队任务列表中的任务之间存在“差距”......
实际上,与其等待扫描完成,我更喜欢使用事件处理程序(另外,我宁愿不使用Application.ProcessMessages
,因为我在无表单应用程序中也需要它)并且我已经尝试过两者IOmniTaskControl.OnTerminated
并且使用 a TOmniEventMonitor
but 因为这些火为每一个完成的任务我仍然需要以某种方式检查当前的是否是最后一个,这再次归结为与上述相同的问题。
或者有没有更好的方法来创建可以避免这个问题的任务?