多线程文件搜索C#

时间:2011-06-03 16:37:02

标签: c# multithreading

我需要一些帮助。现在我已经完成了一个文件搜索,它将搜索我的整个硬盘并且它可以工作。以下是实现它的两种方法。

public void SearchFileRecursiveNonMultithreaded()
    {
        //Search files multiple drive

        string[] drives = Environment.GetLogicalDrives();

        foreach (string drive in drives)
        {
            if (GetDriveType(drive).ToString().CompareTo("DRIVE_FIXED") == 0)
            {
                DriveInfo driveInfo = new DriveInfo(drive);

                if (driveInfo.IsReady)
                {
                    System.IO.DirectoryInfo rootDirectory = driveInfo.RootDirectory;
                    RecursiveFileSearch(rootDirectory);
                }
            }
        }
        MessageBox.Show(files.Count.ToString());
    }

    public void RecursiveFileSearch(DirectoryInfo root)
    {
        DirectoryInfo[] subDirectory;
        try
        {
        //private List<FileInfo> files = new List<FileInfo>() is declared above
            files.AddRange(root.GetFiles(searchString.Text, SearchOption.TopDirectoryOnly));
        }
        catch (Exception)
        {
        }

        try
        {
            // Now find all the subdirectories under this directory.
            subDirectory = root.GetDirectories();

            foreach (System.IO.DirectoryInfo dirInfo in subDirectory)
            {
                // Resursive call will be performed for each subdirectory.
                RecursiveFileSearch(dirInfo);
            }
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString());
        }
    }

现在我正在尝试实施并行搜索以加快搜索速度。我尝试了几个程序来实现这一点。试图使用backgroundworker以及线程,但有问题,调试很难知道什么是错的?有人能让我知道实现并行搜索的方法。这个步骤我会自己去弄清楚。所提供的任何帮助都会得到很大的帮助。

4 个答案:

答案 0 :(得分:5)

首先,正如其他人所指出的那样,当您只搜索一个驱动器时,使用多个线程不太可能加快速度。您的绝大部分时间都花在等待磁盘头移动到需要的位置,并且它一次只能在一个地方。在这里使用多个线程是浪费精力,并且很可能实际上使你的程序变慢。

其次,只需调用Directory.EnumerateFiles即可简化代码。如果您想同时搜索多个驱动器,只需启动多个BackgroundWorker实例,每个实例使用EnumerateFiles搜索不同的驱动器。

但请注意,EnumerateFiles如果跨目录权限问题运行会引发异常(代码也会如此),这在搜索整个驱动​​器时并不常见。如果这是一个问题(可能会是),那么你必须编写自己的目录搜索器。其中一个就是this question的答案。

答案 1 :(得分:1)

您的外圈foreach (string drive in drives)可以从更改为Parallel.ForEach()获得。

你的内循环(RecursiveFileSearch())应该并行,你只会失去性能。但是从Fx4中,您可以将GetFiles()替换为EnumerateFiles(),以便在非常大的文件夹上获得更好的结果。

这解决了大部分踏板安全问题,外部循环应该为每个要填充的驱动器提供一个列表(非异步)。然后,在ForEach()之后合并这些列表。

确切的答案更加困难:并行搜索逻辑磁盘无济于事,增益将来自独立的“车轴”。但是在一个大的RAID卷上,搜索文件可能会受益于一些额外的线程。

答案 2 :(得分:1)

虽然同时搜索逻辑驱动器可能有助于或损害性能,但以下是管理线程的方法:

    using System.Threading;
    ...

    string[] drives = Environment.GetLogicalDrives();
    List<Thread> threads = new List<Thread>();
    foreach (string drive in drives)
    {
        if (GetDriveType(drive).ToString().CompareTo("DRIVE_FIXED") == 0)
        {
            DriveInfo driveInfo = new DriveInfo(drive);

            if (driveInfo.IsReady)
            {
                System.IO.DirectoryInfo rootDirectory = driveInfo.RootDirectory;
                var thread = new Thread((dir) => RecursiveFileSearch((DirectoryInfo)dir));
                threads.Add(thread);
                thread.Start(rootDirectory);
            }
        }
    }
    foreach(var t in threads) t.Join();
    MessageBox.Show(files.Count.ToString());

不要忘记锁定RecursiveFileSearch使用的任何共享集合。您应该尝试避免此类访问,因为它会产生争用。

答案 3 :(得分:0)

使其成为多线程的一种解决方案是将对RecursiveFileSearch的每个调用转储到ThreadPool.QueueUserWorkItem中,以使其在多个线程上运行。

现在,请注意这种方法,原因如下:

1)正如Dypple所说,访问驱动器是单线程的,所以这实际上可能会影响性能

2)列表不是线程安全的,因此在添加到列表之前需要对其进行锁定/同步。这也可能会影响性能。考虑使用System.Collections.Concurrent.ConcurrentBag(在.NET 4.0中)让它为您控制同步,因为您只是在做补充。

3)如果您的MaxIntFiles大于MaxIntFiles,则将您遇到的每个文件添加到列表中都可能导致溢出。

4)此文件集可能会变得庞大,并可能导致内存不足异常。