2013-06-05 69 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 :) –