2012-08-16 81 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