QFutureWatcher, как смотреть несколько задач / фьючерсов и готовых () сигналов
У меня есть несколько задач, началось с QtConcurrent::run()
, Задачи имеет QFutureWatcher
, я знаю это QFutureWatcher
могу смотреть только одно будущее, но когда я запускаю те же задачи из пользовательского интерфейса, как я могу создать `QFutureWatcher* для каждой задачи? Это:
QFutureWatcher<void> *watcher = new QFutureWatcher;
watcher->setFuture(future);
QVector<QFutureWatcher<void>*> watchers;
watchers.push_back(watcher); // vector with pointers to each tasks watchers
или что-то другое?* Моя проблема в том, что я запускаю каждую задачу из своего кода, но задачи выполняются с использованием внешних функций DLL. И у меня нет API, чтобы остановить задачу. Я могу только ждать, пока задача закончится, и закрыть задачу, выполнив все связанные данные, после того, как я получу, закончил QFutureWatcher
сигнал.
В данный момент я использую два QFutureWatchers - один наблюдает за первой запущенной задачей, другой - запускается, если первая задача еще не завершена, и наблюдаю за второй задачей с временным наблюдателем и временным слотом (код такой же, я создаю его только для получения другого наблюдателя). сигнал с соответствующими данными), пока первая задача не закончена. И он смотрел тот же код, но я должен продублировать его, чтобы дождаться, когда первое задание закончено, потому что QFutureWatcher
не имеет очереди с законченными сигналами и связанными watcher.result () `s..
1) Есть ли в Qt какой-либо механизм задач в очереди? или как я могу поймать несколько задач на будущее?
2) QtConcurrent::run
не имеет слота cancel(). Могу ли я использовать функции map/mapped с одним элементом последовательности? Имеет ли отображенный API очередь обработки задач / завершенных сигналов? Как я могу это понять? Спасибо!
2 ответа
1) Вы можете использовать что-то вроде этого (просто черновик кода):
class MultiWatcher
: public QThread
{
Q_OBJECT
signals:
void allDone();
public:
void run() override;
QFutureSynchronizer _sync;
};
void MultiWatcher::run()
{
_sync.waitForFinished();
emit allDone();
}
class Test
: public QObject
{
Q_OBJECT
public:
//...
void do();
void onDone();
};
void Test::do()
{
// fill some futures
auto w = new MultiWatcher();
w->_sync.addFuture( f1 ); // Not thread-safe
w->_sync.addFuture( f2 );
w->_sync.addFuture( f3 );
// ...
connect( w, &MultiWatcher::allDone, this, &Test::onDone );
w->start();
}
void Test::onDone()
{
sender()->deleteLater();
qDebug() << "All futures are done";
}
//...
test->do();
2) есть QFuture::cancel()
Метод для сопоставленных расчетов. Из вашего вопроса непонятно, как вы получаете фьючерсы, поэтому сложно сказать больше. Вы можете прочитать о параллелизме в Qt и задать больше вопросов, если они у вас будут.
QFuture::cancel
не будет работать с QtConcurrent::run
вам нужно спроектировать другой механизм прерывания внутри вашего потока кода. Например:
std::atomic_bool _isRunning = true; // somewhere in code
void MyWorker::doWork()
{
while (_isRunning)
{
// Do next step of computation
}
}
// Use _isRunning = false; to interrupt. It's thread-safe.
Но рискованно прерывать код, которым вы не управляете (из внешних.dll), потому что у вас может быть много утечек. Вот почему в QtConcurrent нет API завершения.
PS Пожалуйста, не пишите, что я делаю это неправильно с QThread. Там все хорошо. Я знаю, что я делаю. Это хороший образец, где наследование QThread
все в порядке.
Есть ли в Qt какой-либо механизм задач в очереди?
Qt предоставляет мощную структуру сигналов и слотов, которая идеально подходит для такого применения.
Шаги реализации:
- Создайте рабочий класс (производный от QObject), в котором есть слот
executeTask(int foo, QString bar)
который содержит реализацию задачи. - Создать
QThread
и переместите ваш экземпляр класса Worker в этот QThread, используяQObject::moveToThread
- Начать тему
- Запускайте задачи, испуская сигнал, который подключен к
executeTask
или по телефонуQMetaMethod::invoke
наexecuteTask
метод.
Заметки:
- Qt поставит ваши звонки в очередь
executeTask
(при условии, что вы используете один из методов, описанных в 4.), и они будут выполняться по порядку. - Чтобы быстро терминировать вашего работника, вы можете использовать
QThread::requestInterruption
и проверьте отмену в вашемexecuteTask
метод с использованиемQThread::isInterruptionRequested
, - Для принудительного увольнения вашего работника вы можете использовать
QThread::terminate
, - В противном случае используйте
QThread::quit
а такжеQThread::wait
изящно уволить работника и ждать, пока все задачи не будут выполнены.