C#中的多线程目录循环

时间:2010-07-20 19:16:47

标签: c# .net recursion

我正在尝试遍历所有文件和文件夹,并对具有特定扩展名的所有文件执行操作。这种方法工作正常,但我想让它多线程,因为当完成数以万计的文件时,它真的很慢,我会使用多线程进行成像会加快速度。在这种情况下,我只是不确定如何使用线程。

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)
        {
        }
    }

5 个答案:

答案 0 :(得分:5)

假设doStuff是线程安全的,并且您不需要等待整个扫描完成,您可以在ThreadPool上同时调用doStuffscan,像这样:

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线程分开的线程上执行此类工作是一种很好的做法。这样,当你的应用程序正在繁重时,用户界面仍会保持响应。

*我假设您的scandoStuff方法实际上是在硬盘上读取和/或写入数据。如果不是这种情况,那么并行化这段代码可能会有意义。

答案 2 :(得分:1)

doStuffscan究竟做了什么?除非它们非常占用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。

非常感谢大家!