扁平化层次结构 - 一个生产者线程以获取文件夹及其子文件夹中的所有文件






3.25/5 (4投票s)
使用生产者线程扁平化层级结构,以获取文件夹和子文件夹中的所有文件。
引言
本文演示了一种“扁平化”层级结构的技术,使其看起来像一个简单的列表。演示了几种有趣的编码技术,包括线程以及如何进行 WIN32 调用。我在开发一个图像查看器时开始思考层级结构。 在我的例子中,使用递归很容易遍历层级结构,即一个包含子文件夹和其中文件的文件夹,但递归通常与您编写的软件的其他部分的设计不太吻合。 任何编写过打印代码的人都会明白我的意思。
假设您要打印公司所有人员的组织结构图,从 CEO 开始,然后是直线经理,对于每个直线经理,打印为她工作的人员等等。 现在 Windows 调用你一次打印一页。 在第二页上,你需要记住你进行到哪里了,如果你尝试使用递归来遍历这样的层级结构,你就会遇到麻烦,因为你需要尝试在第一页末尾重新创建堆栈的状态!
所以你可以尝试将所有人员放入一个简单的列表,然后使用该列表来打印你的报告。 但是,在层级结构非常大的情况下,由于缺乏内存,并不总是可以将层级结构中的项目放入列表。 你的组织只有三百人工作? 好的聪明人,我会让它更难 - 打印一个组织结构图,对于每个人,打印他们参与的每个项目,对于每个项目,打印所有客户,对于每个客户......好吧,你明白了!
所以我开发了一种使用线程的技术,可以完成这项工作。
所需的模型
以下代码片段显示了我希望能够编写的那种代码
void showFiles(string path)
{
Flattener f(path);
foreach (string fn in f)
{
Console.WriteLine(fn);
}
}
通过将复杂性隐藏在 Flattener 类中,不仅代码更容易阅读,而且更加通用。线程来救援
实际上只有一个线程。看看下面的代码片段
void showFiles(string path)
{
foreach (string fn in Directory.GetFiles(path))
{
Console.WriteLine(fn);
}
foreach (string folder in Directory.GetFolders(path))
{
showFiles(folder);
}
}
标准的东西。现在假设我们让它在单独的线程上运行,并稍微更改一下。
void showFiles(string path)
{
foreach (string fn in Directory.GetFiles(path))
{
m_filename = fn;
wait();
}
foreach (string folder in Directory.GetFolders(path))
{
showFiles (folder);
}
}
我们现在需要做的就是编写 `wait()` 函数,以等待 Monitor 直到主线程想要下一个项目。证明完毕!
过早停止线程
如果用户想在耗尽所有文件之前关闭,我需要一种方式来通知线程停止,所以我编写了一个 `endThread` 函数。public void endThread()
{
lock(this)
{
m_askedToClose = true;
Monitor.Pulse(this);
return;
}
}
当线程唤醒时,它可以检查布尔值。 我发现抛出异常是展开递归的最简单方法。枚举器
您可能会注意到我还从 `IDisposable` 派生了我的枚举器。 我这样做是因为我需要在有人完成枚举器后结束线程。 但我最终得到了一些丑陋的代码。FolderFlattener ff = new FolderFlattener(m_initialFolder);
using (IDisposable d = ff.GetEnumerator() as IDisposable)
{
IEnumerator en = d as IEnumerator;
etc...
}
我不太喜欢这样,所以我创建了自己的接口,该接口派生自两者public interface ImyOwnEnumerator : IEnumerator, IDisposable { }
并按如下方式使用它FolderFlattener ff = new FolderFlattener(m_initialFolder);
using (ImyOwnEnumerator me = ff.GetEnumerator() as ImyOwnEnumerator)
{
etc...
}
我认为这看起来更漂亮一些。WIN32 调用
我完全沉迷其中,开始不喜欢 `Directory.GetFiles` 的工作方式。 我认为调用 WIN32 `FindFirstFile` 等可能会很有趣。 在让它工作后,我想知道是什么让我一开始就启动了,但记不起来了。 无论如何,代码仍然在那里,所以如果你对如何调用 WIN32 函数不感兴趣,请忽略它。致谢
感谢 Wesner Moyse 的部分代码 - 请参阅 "A Win32 Library for .Net" www.codeproject.com/csharp/Win32.asp。Frank Eden
Frank 自 1973 年以来一直在计算机领域工作,有时会假装自己是业主建造商。 在过去的 15 年里,他一直在 TOWER Software 工作,开发 TRIM,(http://www.towersoft.com.au/) 这是世界上首屈一指的文档管理软件。您可以通过电子邮件 frank.eden a@t towersoft.com.au 联系 Frank。