Meteo ir3
Meteo ir3

Reputation: 449

QFutureWatcher how to watch multiple tasks/futures and finished() signals

I have some tasks, started with QtConcurrent::run(). Tasks has QFutureWatcher. I know that QFutureWatcher can watch only one Future, but when I started the same tasks from UI, how can I create `QFutureWatcher* on each task? Is it :

QFutureWatcher<void> *watcher = new QFutureWatcher;
watcher->setFuture(future);
QVector<QFutureWatcher<void>*> watchers;
watchers.push_back(watcher); // vector with pointers to each tasks watchers

or something else? * My trouble is that I start each task from my code, but tasks executed with external DLL functions. And I have no API to stop the task. I can only wait for task is over and close task with release all related data, after I had received finished QFutureWatcher signal.

At this moment, I use two QFutureWatchers - one is watching the first runned task, another - starts if first task is not over and watching the second task with temporary watcher and temporary slot (code the same, I creates it just for receive another watcher signal with related data) until first task is over. And it watched the same code, but I must to duplicate it for waiting first task is over, because QFutureWatcher doesn't have queue with finished signals and related watcher.result()`s ..

1) Does Qt has any Queued task mechanism? or how can I catch multiple tasks futures?

2) QtConcurrent::run doesn't have cancel() slot. May I use map/mapped functions with one sequence item? Does mapped API has queued processing of tasks/finished signals? How I can realize that? Thx!

Upvotes: 3

Views: 4709

Answers (2)

Timo
Timo

Reputation: 1814

Does Qt has any Queued task mechanism?

Qt provides a powerful signal&slot framework, which is perfect for such an application.

Implementation Steps:

  1. Create a Worker Class (QObject derived) which has a slot executeTask(int foo, QString bar) that contains the task implementation.
  2. Instantiate a QThread and move your Worker class instance to that QThread using QObject::moveToThread
  3. Start the Thread
  4. Run Tasks by either emitting a signal which is connected to executeTask or by calling QMetaMethod::invoke on the executeTask method.

Notes:

  • Qt will queue your calls to executeTask (as long as you use one of the methods described in 4.) and they will be executed in order.
  • To quickly termiante your Worker you could use QThread::requestInterruption and check for cancellation in your executeTask method using QThread::isInterruptionRequested.
  • To force terminate your Worker you could use QThread::terminate.
  • Otherwise use QThread::quit and QThread::wait to gracefully terminate the worker and wait till all Tasks have been executed.

Upvotes: 1

Dmitry Sazonov
Dmitry Sazonov

Reputation: 9014

1) You may use something like this (just code draft):

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) There is QFuture::cancel() method for mapped calculations.It is not clear from your question, how do you get futures, so it is hard to say more. You may read about concurrency in Qt and ask more questions, if you will have some.

QFuture::cancel will not work with QtConcurrent::run, you need to design another interruption mechanism inside your thread code. For example:

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.

But it is risky to interrupt code that you don't manage (from external .dll), because you may have a lot of leaks. That's why there is no "terminate" API in QtConcurrent.

P.S. Please, don't write that I'm do it wrong with QThread. There are everything good. I know what I'm doing. This is a good sample, where inheritance of QThread is OK.

Upvotes: 3

Related Questions