如何使用生产者/消费者队列进行文件夹和文件的递归搜索?

3

我希望先搜索目录,然后再搜索其中的文件,查找关键字。

我知道我需要两个类:生产者类和消费者类,但我不知道如何使用C#生产者/消费者队列进行搜索?

public class Program
{
    private static void Main()
    {
        Queue<File> searchFile = new Queue<File>();
        Queue<Directory> searchDirectory = new Queue<Directory>();

        new Thread(searchDirectory).Start();

        for (int i = 0; i < 3; i++)
            new Thread(searchFile).Start();
    }
}

这是一个非常好的问题。 - Chris Marisic
4个回答

1

初始问题:

  1. 您正在使用相同的变量名称和作用域声明两个不同类型的变量。
  2. 您不想在目录上启动一个线程搜索,另一个线程搜索文件。

第二个问题是,您正在与多个线程中最大的瓶颈之一——即磁盘IO进行对抗。通过实现多个工作线程,在标准HDD设备上执行磁盘IO将毫无收获。

请更详细地解释您尝试做什么(请提供示例)。可能有更好的处理过程。


1

首先,Directory 是一个静态类,因此您将无法将其添加到集合中。您需要使用 DirectoryInfo 代替。其次,我建议使用单个队列来保存 DirectoryInfo 实例。然后可以在处理单个文件夹时枚举文件。

以下是我使用生产者-消费者模式的实现方式。此实现使用 BlockingCollection 类,它是阻塞队列的一种实现。阻塞队列在生产者-消费者模式中非常有用,因为它们几乎抽象了所有生产者-消费者细节。

public class Searcher
{
  private BlockingCollection<DirectoryInfo> m_Queue = new BlockingCollection<DirectoryInfo>();

  public Searcher()
  {
    for (int i = 0; i < NUMBER_OF_THREADS; i++)
    {
      var thread = new Thread(Run);
      thread.IsBackground = true;
      thread.Start();
    }
  }

  public void Search(DirectoryInfo root)
  {
    m_Queue.Add(root);
  }

  private void Run()
  {
    while (true)
    {
      // Wait for an item to appear in the queue.
      DirectoryInfo root = m_Queue.Take();

      // Add each child directory to the queue. This is the recursive part.
      foreach (DirectoryInfo child in root.GetDirectories())
      {
        m_Queue.Add(child);
      }

      // Now we can enumerate each file in the directory.
      foreach (FileInfo child in root.GetFiles())
      {
        // Add your search logic here.
      }
    }
  }
}

我应该指出,大多数磁盘以更加串行化的方式工作,因此除非您的逻辑中与CPU绑定的部分非常复杂,否则使用多个线程尝试搜索文件可能不会带来太多好处。

1

正如其他帖子所提到的,多个线程尝试执行IO操作会导致问题。然而,它们可以用于构建完整的目录队列(如果它非常深)然后启动一个单独的线程来对文件进行正则表达式匹配。就像这样:

    class Program
{
    static void Main(string[] args)
    {
        ConcurrentQueue<DirectoryInfo> concurrentQueue = new ConcurrentQueue<DirectoryInfo>();
        GetAllDirectories(new DirectoryInfo(@"C:\local\oracle"), concurrentQueue);
        Action action = () =>{
            const string toFind = "ora";
            DirectoryInfo info;
            while(concurrentQueue.TryDequeue(out info))
            {
                FindInFile(toFind, info);
            }
        };

        Parallel.Invoke(action, action, action, action);
        Console.WriteLine("total found " + _counter);
        Console.ReadKey();
    }
    static int _counter = 0;
    static void FindInFile(string textToFind,DirectoryInfo dirInfo)
    {
        var files =dirInfo.GetFiles();
        foreach(FileInfo file in files)
        {
            using (StreamReader reader = new StreamReader(file.FullName))
            {
                string content = reader.ReadToEnd();

                Match match = Regex.Match(content, textToFind, RegexOptions.Multiline);

                if(match.Success)
                {
                    Interlocked.Increment(ref _counter);
                    Console.WriteLine(file.FullName + " found " + match.Captures.Count);

                    foreach(var t in match.Captures)
                    {
                        Console.WriteLine("-------------> char index" + match.Index);
                    }
                }
            }
        }
    }

    internal static void GetAllDirectories(DirectoryInfo root, ConcurrentQueue<DirectoryInfo> values)
    {
        foreach (var di in root.GetDirectories())
        {
            GetAllDirectories(di, values);
            values.Enqueue(di);
        }
    }
}

0

我已经编辑了这篇文章(正在等待同行评审)。如果它被批准,我已经编辑了代码以修复基本的作用域和拼写错误,但我认为你还没有准备好进行多线程编程,更不用说生产者-消费者队列了(天知道我已经涉足多线程编程一段时间了,但我仍然会搞砸我的实现,但这可能只是我的问题!)。

首先,你应该先熟悉作用域和多线程编程。特别是要阅读关于锁定机制/并发问题的内容,这对于实现成功的多线程解决方案至关重要。

其次,正如IAbstract所建议的那样,确实要使用互斥量/信号量来实现多线程,以获得多线程性能和所需的生产者-消费者队列。

此外,如果你感到舒适,你也可以查看最新的Async CTP1 DataFlow库,它使用Tasks Parallel Library支持此模式。或者你可以使用BlockingCollection来实现此模式。

Stackoverflow上也有一些围绕你的问题的问题,并给出了一些很好的答案。只需搜索“producer-consumer”即可阅读它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接