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 предоставляет мощную структуру сигналов и слотов, которая идеально подходит для такого применения.

Шаги реализации:

  1. Создайте рабочий класс (производный от QObject), в котором есть слот executeTask(int foo, QString bar) который содержит реализацию задачи.
  2. Создать QThread и переместите ваш экземпляр класса Worker в этот QThread, используя QObject::moveToThread
  3. Начать тему
  4. Запускайте задачи, испуская сигнал, который подключен к executeTask или по телефону QMetaMethod::invoke на executeTask метод.

Заметки:

  • Qt поставит ваши звонки в очередь executeTask (при условии, что вы используете один из методов, описанных в 4.), и они будут выполняться по порядку.
  • Чтобы быстро терминировать вашего работника, вы можете использовать QThread::requestInterruption и проверьте отмену в вашем executeTask метод с использованием QThread::isInterruptionRequested,
  • Для принудительного увольнения вашего работника вы можете использовать QThread::terminate,
  • В противном случае используйте QThread::quit а также QThread::wait изящно уволить работника и ждать, пока все задачи не будут выполнены.
Другие вопросы по тегам