2012-08-16 46 views
0

我正在编写一个程序,它利用线程池来搜索指定扩展名的文件以匹配正则表达式。线程池的C++ std ::线程停止条件

我的线程池是这样的:

for(int i = 0; i < _nThreads; ++i) 
    { 
      _threads.push_back(thread(&ThreadPool::GrepFunc, this)); 
    } 

和运行功能如下:

void ThreadPool::GrepFunc() 
{ 
    // implement a barrier 

while(!_done) 
{ 
    while(!_tasks.empty()) 
    { 
     fs::path task; 
     bool gotTask = false; 
     { 
      lock_guard<mutex> tl(_taskMutex); 
      if(!_tasks.empty()) 
      { 
       task = _tasks.front(); 
       _tasks.pop(); 
       gotTask = true; 
      } 
     } 

     if(gotTask) 
     { 
      if(std::tr2::sys::is_directory(task)) 
      { 
       for(fs::directory_iterator dirIter(task), endIter; dirIter != endIter; ++dirIter) 
       { 
        if(fs::is_directory(dirIter->path())) 
        { 
         { lock_guard<mutex> tl(_taskMutex); 
         _tasks.push(dirIter->path()); } 
        } 
        else 
        { 
         for(auto& e : _args.extensions()) 
         { 
          if(!dirIter->path().extension().compare(e)) 
          { 
           SearchFile(dirIter->path()); 
          } 
         } 
        } 
       } 
      } 
      else 
      { 
       for(auto& e : _args.extensions()) 
       { 
        if(!task.extension().compare(e)) 
        { 
         SearchFile(task); 
        } 
       } 
      } 
     } 
    } 
} 
} 

本质上的程序接收来自用户的初始目录,将递归通过它可以搜索和所有与扩展匹配的文件的子目录查找正则表达式匹配。我无法确定如何确定何时达到_done的停止情况。我需要确保初始目录中的所有目录和文件都已被扫描,并且在我加入线程之前,_tasks中的所有项目都已完成。任何想法真的会被赞赏。

回答

1

我建议有一个线程(可能是同一个线程产生的文件处理线程)专门做递归文件系统搜索匹配文件;它可以将文件添加到工作队列中,文件搜索线程可以从中读取工作。你可以使用一个条件变量来协调这个。

协调关机有点棘手,正如您找到的那样。在文件系统搜索线程完成搜索之后,它可以设置一些“刚刚完成对队列可见的标记”标志,然后发信号通知它们全部醒来并尝试处理另一个文件:如果他们发现文件/工作队列为空他们退出。文件系统搜索线程然后加入所有工作人员。

+0

我知道这是有效的,因为这实际上是我在我的程序的第一个版本中实现的。我只用了一个使用recursive_directory_iterator的单个循环,它执行扫描并将匹配扩展的文件传递给工作线程。然而,通过测试更大的目录,我发现大部分运行时间实际上都花在了递归搜索上,所以我一直在尝试线程化搜索以及线程和优化的练习。如果我找不到解决方案,我会回头看看,但我真的希望找到一种方法来完成这项工作。 – 2012-08-16 03:00:55

+0

那么,我建议单个文件系统搜索线程的原因是多线程你可能会发现你的磁盘头跳来跳去,最终导致性能下降,但这取决于你使用的磁盘技术:严重扫描的磁盘将有更好的并发性,SSD更好地寻求时代。所以是的 - 你可以创建另一个子目录队列来搜索......另一个工作线程池来扫描这些子目录并将子子目录添加到队列中。 – 2012-08-16 03:25:09

0

关于托尼答案的评论中更新的问题,我建议有两种任务:一种用于递归探索子目录,另一种用于查找grep。您需要SynQueue<TaskBase>TaskSubDir: TaskBaseTaskGrep: TaskBaseTaskBase有一个虚拟接口functon Run()。然后线程可以从SynQueue反复弹出,并调用TaskBase::Run()

  1. ,如果它有一个TaskSubDir,那么它会发现 子目录和文件,在给定的路径: (一),如果它是一个文件夹,将子目录的新的TaskSubDir添加到SynQueue,以便使用线程池递归搜索文件夹; (b)如果它是匹配 扩展名的文件,则它会将TaskGrep推送到SynQueue
  2. 如果它得到了TaskGrep,那么它执行SearchFile
  3. 如果队列为空,break出工人职能。

这样做,在启动grep队列之前,您不需要有2个队列并等待子目录队列完成。

所以回答你的问题:为了确定加入条件,你需要做的就是等待所有线程break出工人函数。

最后说明:代码中的第一个_tasks.empty()不受互斥锁保护,可能会遇到竞争条件。我建议你在SynQueue类中隐藏互斥锁和cond_var,并添加一个SynQueue::empty()成员函数(受互斥锁保护)。如果效率是你关心的问题,你可能要考虑免锁队列来代替SynQueue

+0

我对你的回答有点困惑,因为我不太清楚它与我的不同。现在,我没有单独排队。所有任务与路径对象都在同一个队列中,我可以通过检查它们是否是目录来简单地处理它们,因此对这种级别的抽象类型的需求似乎没有必要。另外,如果我使用队列空条件来解决问题,我怎样才能保证程序实际上已经完成搜索?时间可能潜在地解决检查是在没有任何任务存在的时候完成的,但是即将被添加。 – 2012-08-16 04:54:00

+0

@JesseCarter,主要区别在于你的工作函数在一个大的'if(gotTask)'子句中处理子文件夹和文件;而我把它分解成更小的任务。这样做可以有效地解决您提到的问题:某些线程突破了辅助函数,而实际上并非所有任务都已处理完毕。 – user2k5 2012-08-16 05:31:59