2013-06-05 68 views
2

我正在嘗試使用多線程方法執行遞歸目錄列表。當將異步調用替換爲普通單線程遞歸函數調用時,以下代碼可以正常工作,但在使用異步實現時,遞歸啓動的線程似乎在從主完成的初始異步調用完成時終止,因爲輸出顯示多次調用函數但是輸出所有文件的唯一目錄是最初的目錄,「已完成」只輸出一次,儘管「已啓動」多次輸出,並且還輸出了其他目錄的文件。我懷疑我缺少一些根本性的東西。任何人都可以解釋這段代碼有什麼問題嗎?C++異步線程在調用線程完成時終止

#include <filesystem> 
#include <future> 
#include <functional> 
#include <concurrent_vector.h> 
#include <concurrent_queue.h> 
#include <iostream> 

using namespace std; 
using namespace std::tr2::sys; 
using namespace concurrency; 

concurrent_vector<future<void>> taskList; 

void searchFiles(wstring path, concurrent_queue<wstring>& fileList) 
{ 
    wcout << L"Started " << path << endl; 
    wdirectory_iterator directoryIterator(path); 
    wdirectory_iterator endDirectory; 
    for(; directoryIterator != endDirectory; ++directoryIterator) 
    { 
     wcout << path + L"/" + (wstring)directoryIterator->path() << endl; 
     if (is_directory(directoryIterator->status())) 
     { 
      taskList.push_back(async(launch::async, searchFiles, path + 
      L"/" + (wstring)directoryIterator->path(), ref(fileList))); 
     } 
     else 
     { 
      fileList.push(path + L"/" + (wstring)directoryIterator->path()); 
     } 
    } 
    wcout << L"Finished " << path << endl; 
} 

int main() 
{ 
    concurrent_queue<wstring> fileList; 
    wstring path = L".."; 
    taskList.push_back(async(launch::async, searchFiles, path, ref(fileList))); 
    for (auto &x: taskList) 
     x.wait(); 
} 

順便說一句可能會問爲什麼我不使用wrecursive_directory_iterator。顯然wrecursive_directory_iterator會拋出一個異常,如果你沒有讀取權限,將無法繼續,所以這種方法應該允許你繼續這種情況。

+0

我想知道你爲什麼要用多線程來做這件事。這有可能會使你的磁盤出現問題。 – paddy

+0

正如你可能猜想的那樣,這是一個用於併發編程的學校項目,所以這是使用多線程方法的要點。此外,Visual Studio concurrent_vector和concurrent_queue容器與問題無關。一旦我找出這個問題,他們只能在未來進行進一步的工作並找到文件。 –

回答

2

問題是基於範圍的for循環。

如果我們看看如何定義range-based for statement,我們會看到循環的最終迭代器只能計算一次。在進入循環的時候,可能(這是一場比賽)只有一個向前的向前(你在上面一行中推回的那個)。因此,在任務完成後,迭代器將遞增並等於舊的末端迭代器,並且即使向量現在可能包含更多元素,這些元素在第一個任務中被推回,循環也會結束。還有更多的問題。

在完成循環後將被調用的向量的析構函數通常應該調用它的所有元素的析構函數,對於將來的std::async將來將等於調用wait,儘管您仍然向vector中添加元素,已經在其破壞者,這可能是UB。

另一點是,當您在第一個線程中push_back到vector時,您在輸入for循環時創建的end-iterator將會失效,這意味着您正在對無效的迭代器進行操作。

作爲一個解決方案,我建議避開全局任務列表,而是在您的searchFiles函數中使用本地任務列表,然後您可以在每個級別的searchFiles函數中等待所有本地期貨。這是非管理遞歸併行性中的一種常見模式。

注意:我不知道ppl concurrent_vector的所有細節,但我認爲它的行爲與std::vector類似。

+0

我認爲你是對的,但我不確定顯示基於範圍的'for'循環的定義是否有幫助,而_「這意味着循環將只遍歷向量中的第一個也是唯一的未來。 _可以更清楚地解釋。 **爲什麼**這是否意味着? –

+0

@Jonathan Wakely好的,謝謝你的反饋。稍後我會添加更新。 – inf

+0

我喜歡更新的版本,如果可以的話,我會再次upvote :) –