В сущности, async - это thread + promise/future с удобствами: его не нужно join-нить руками, не нужно создавать промежуточный promise, а возвращенный функцией результат и исключения можно получить в вызывающем коде. При этом деструктор объекта future, полученного в результате вызова async, блокирует вызывающий поток до завершения нашей async-функции.
В нашем случае разницы с использованием thread в части отправки сигналов нет, ведь нам по-прежнему придется сигнализировать виджету о завершении сканирования. Это можно сделать в самой функции сканирования. Если я правильно понял, раньше мы это делали в функции scanFinished, которая вызывалась в конце функции сканирования.
Под задачами обычно понимают независимые потоки выполнения (необязательно параллельно), которые можно запустить и забыть, а в конце забрать результат. Потоки сканирования директорий, судя по коду из вопроса, не зависят друг от друга, и мы наш ThreadManager можем разбить на две переменные типа ScanTask
#include <iostream>
#include <future>
#include <atomic>
#include <memory>
#include <string>
class ScanTask {
public:
struct Result {
enum {
Success, Failure, Canceled
} status;
std::string message;
};
ScanTask(const std::string& dir)
: canceled_{ false },
result_{std::async(&ScanTask::scan, this, dir)}
{ }
void cancel() {
canceled_ = true;
}
Result result() {
return result_.get(); // result_.valid() == false
}
Result scan(const std::string& dir) {
struct Finalize {
~Finalize() {
// emit signal scan completed
}
} finalizeSignal;
while (!canceled_ /* && not done */) {
// .. scan subdirectories
/*
if (something failed)
return {Result::Failure, "Failure"};
*/
}
if (canceled_)
return { Result::Canceled, std::string("[Canceled] ") + dir };
return { Result::Success, std::string("[Success] ") + dir };
}
private:
std::atomic<bool> canceled_;
std::future<Result> result_;
};
int main() {
auto localScanTask = std::make_unique<ScanTask>("a");
localScanTask->cancel();
std::cout << localScanTask->result().message << std::endl;
auto sharedScanTask = std::make_unique<ScanTask>("b");
sharedScanTask->cancel();
std::cout << sharedScanTask->result().message << std::endl;
return 0;
}
Можно было даже не создавать отдельный класс, а в функции-обработчике нажатия кнопки запуска присваивать future члену виджета результат вызова async от анонимной лямбды, как предлагали с thread в ревью. При этом atomic canceled_ можно захватить по ссылке, она была бы общей для всех.