c++multithreadingqtqtconcurrentqfuture

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!


Solution

  • 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.