многопоточность — асинхронные потоки C ++ завершаются, когда завершается вызывающий поток

Я пытаюсь сделать рекурсивный список каталогов, используя многопоточный подход. Следующий код работает нормально, когда заменяет асинхронные вызовы обычным однопоточным рекурсивным вызовом функции, но при реализации с асинхронными рекурсивно запущенными потоками все, кажется, завершаются, когда завершается первоначальный асинхронный вызов, сделанный из main, так как вывод показывает несколько вызовов функции стартовый, но единственный каталог, в который выводятся все файлы, является исходным, а «Завершено» выводится только один раз, хотя «Запущено» выводится несколько раз, а также выводятся файлы некоторых других каталогов. Я подозреваю, что мне не хватает чего-то фундаментального. Может кто-нибудь объяснить, что не так с этим кодом?

#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 сгенерирует исключение и остановится, не имея возможности продолжить, если у вас нет разрешения на чтение, поэтому этот метод должен позволить вам продолжить в этом случае.

2

Решение

Проблема заключается в диапазоне для цикла.

Если мы посмотрим на то, как на основе диапазона для заявления определяется, мы видим, что конечный итератор цикла будет вычисляться только один раз. Во время входа в цикл, вероятно (это раса) только одно будущее в вашем векторе (то, которое вы отбросили назад в строке выше). Таким образом, после завершения этой задачи итератор будет увеличен и будет равен вашему старому конечному итератору, и цикл завершится, даже если вектор теперь может содержать больше элементов, которые были перенесены в вашу первую задачу. Есть еще больше проблем с этим.

Деструктор вектора, который будет вызван после завершения цикла, обычно должен вызывать деструктор всех его элементов, которые в будущем из std::async будет равносильно вызову wait, хотя вы все еще добавляете элементы в вектор, пока он уже находится в его деструкторе, который, вероятно, является UB.

Другой момент заключается в том, что конечный итератор, созданный вами при входе в цикл for, будет признан недействительным, как только вы нажмете на ваш вектор в первом потоке, это означает, что вы работаете с недействительными итераторами.

В качестве решения я бы предложил избежать глобального списка задач и вместо этого использовать локальный список задач в вашем searchFiles функции, вы можете ждать на всех ваших местных фьючерсов в вашем searchFiles функционировать на каждом уровне. Это обычная модель неуправляемого рекурсивного параллелизма.

Примечание: я не знаю всех деталей из ppl concurrent_vector, но я предполагаю, что он ведет себя подобно std::vector,

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]