我正在尝试遍历所有文件和文件夹,并对具有特定扩展名的所有文件执行操作。这种方法工作正常,但我想让它多线程,因为当完成数以万计的文件时,它真的很慢,我会使用多线程进行成像会加快速度。在这种情况下,我只是不确定如何使用线程。
doStuff
从文件中读取属性(修改日期等,并将它们插入到sqlite数据库中。我在调用扫描方法之前启动一个事务,以便尽可能地优化它。< / p>
提供关于如何做到这一点的理论的答案与完整的代码答案一样好。
private static string[] validTypes = { ".x", ".y", ".z", ".etc" };
public static void scan(string rootDirectory)
{
try
{
foreach (string dir in Directory.GetDirectories(rootDirectory))
{
if (dir.ToLower().IndexOf("$recycle.bin") == -1)
scan(dir);
}
foreach (string file in Directory.GetFiles(rootDirectory))
{
if (!((IList<string>)validTypes).Contains(Path.GetExtension(file)))
{
continue;
}
doStuff(file);
}
}
catch (Exception)
{
}
}
答案 0 :(得分:5)
假设doStuff
是线程安全的,并且您不需要等待整个扫描完成,您可以在ThreadPool上同时调用doStuff
和scan
,像这样:
string path = file;
ThreadPool.QueueUserWorkItem(delegate { doStuff(path); });
你需要创建一个单独的局部变量,因为匿名方法本身会捕获file
变量,并且会在整个循环中看到它的变化。 (换句话说,如果ThreadPool仅在循环继续下一个文件后执行任务,它将处理错误的文件)
但是,阅读你的评论,这里的主要问题是磁盘IO,所以我怀疑多线程不会有多大帮助。
请注意,对于包含大量文件的目录,Directory.GetFiles
执行速度会很慢。 (因为它需要分配一个数组来保存文件名)
如果您正在使用.Net 4.0,则可以通过调用EnumerateFiles
method来加快速度,它使用迭代器返回IEnumerable<string>
,在运行循环时枚举目录。
您还可以通过传递scan
参数来避免使用任一方法进行递归SearchOption
调用,如下所示:
foreach (string file in Directory.EnumerateFiles(rootDirectory, "*", SearchOption.AllDirectories))
这将递归扫描所有子目录,因此您只需要一个foreach
循环。
请注意,这会加剧GetFiles
的性能问题,因此您可能希望避免使用此版本的.Net 4.0。
答案 1 :(得分:2)
在IO操作上使用多线程通常是一个糟糕的调用*。您可能有多个CPU或具有多个内核的CPU;但通常情况下,您的硬盘无法同时读取或写入多个文件。这类事情通常需要序列化。
也就是说,在与UI线程分开的线程上执行此类工作是一种很好的做法。这样,当你的应用程序正在繁重时,用户界面仍会保持响应。
*我假设您的scan
和doStuff
方法实际上是在硬盘上读取和/或写入数据。如果不是这种情况,那么并行化这段代码可能会有意义。
答案 2 :(得分:1)
doStuff
和scan
究竟做了什么?除非它们非常占用CPU,否则我会认为磁盘访问会成为瓶颈,如果有什么东西使它成为多线程可能会更慢。
答案 3 :(得分:1)
在旁注中,无需将validTypes
转换为IList<string>
,因为数组在.net 3.5 +中实现IEnumerable<T>
。
其次,validTypes
可能更好地实现为HashSet,使用Contains
为O(1)查找而不是O(n)。也就是说,这可能不会影响这种情况下的性能,因为你的应用程序是IO绑定的,正如其他答案所指出的那样。
答案 4 :(得分:0)
感谢所有回复的人。我最终得到的是
foreach (string file in Directory.EnumerateFiles(rootDirectory, "*", SearchOption.AllDirectories))
{
if (!((IList<string>)validTypes).Contains(Path.GetExtension(file)))
{
continue;
}
string path = file;
ThreadPool.QueueUserWorkItem(delegate { doStuff(path); });
}
与之前的多个小时相比,大约需要2分钟。我认为大部分滞后都在数据库中,而不是文件IO。
非常感谢大家!