Solved QtConcurrent and recursion
-
Good day. I tried execute some long operation which use recursion with QtConcurrent::run. But signal finished() of QFutureWatcher not emitted for all calls. Here code:
MainWIndow.h#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QThreadPool> namespace Ui { class MainWindow; } class WatcherHandler: public QObject { Q_OBJECT public slots: void watcherFinished(); }; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_pushButton_clicked(); QString recurse(int level, int count = 0); private: QThreadPool *pool; int MaxLevel; Ui::MainWindow *ui; WatcherHandler *h; }; #endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QtConcurrent> #include <QDebug> #include <QThread> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { pool = nullptr; ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; if(pool) delete pool; } void MainWindow::on_pushButton_clicked() { QThread *thrd = new QThread; h = new WatcherHandler; h->moveToThread(thrd); connect(qApp, &QApplication::aboutToQuit, thrd, &QThread::deleteLater); thrd->start(); pool = new QThreadPool; MaxLevel = 3; //QtConcurrent::run(pool, this, &MainWindow::recurse,0,0); recurse(0); } QString MainWindow::recurse(int level, int count) { qDebug() << "Thread : " << QThread::currentThread() << " : " << thread(); qDebug() << "Start recurse level " << level << count; unsigned long worktime = 1000/(level+1)-level; QThread::currentThread()->msleep(worktime); if(level < MaxLevel) for(int i = 0; i <= level; i++) { QFutureWatcher<QString> *watcher = new QFutureWatcher<QString>; connect(watcher, &QFutureWatcher<QString>::finished, h, &WatcherHandler::watcherFinished, Qt::QueuedConnection); QFuture<QString> f = QtConcurrent::run(pool, this, &MainWindow::recurse, level+1, i); watcher->setFuture(f); } QThread::currentThread()->msleep(worktime); qDebug() << "Finished recurce level" << level << count; return QString("level %1 count %2").arg(level).arg(count); } void WatcherHandler::watcherFinished() { QFutureWatcher<QString> *watcher = static_cast<QFutureWatcher<QString> *> (QObject::sender()); qDebug() << "Thread : " << QThread::currentThread(); qDebug() << "SIGNAL FINISHED EMITED FOR" << watcher->result(); }
And debug log:
Thread : QThread(0x3da9c0) : QThread(0x3da9c0)
Start recurse level 0 0
Thread : QThread(0x42b430, name = "Thread (pooled)") : QThread(0x3da9c0)
Start recurse level 1 0
Thread : QThread(0x463950, name = "Thread (pooled)") : QThread(0x3da9c0)
Thread : QThread(0x463b80, name = "Thread (pooled)") : QThread(0x3da9c0)
Start recurse level 2 0
Start recurse level 2 1
Thread : QThread(0x463db0, name = "Thread (pooled)") : QThread(0x3da9c0)
Start recurse level 3 0
Finished recurce level 1 0
Thread : QThread(0x42b430, name = "Thread (pooled)") : QThread(0x3da9c0)
Start recurse level 3 0
Finished recurce level 0 0
Thread : QThread(0x466890)
SIGNAL FINISHED EMITED FOR "level 1 count 0"
Finished recurce level 2 0
Finished recurce level 2 1
Thread : QThread(0x463950, name = "Thread (pooled)") : QThread(0x3da9c0)
Thread : QThread(0x463b80, name = "Thread (pooled)") : QThread(0x3da9c0)
Start recurse level 3 1
Start recurse level 3 1
Finished recurce level 3 0
Thread : QThread(0x463db0, name = "Thread (pooled)") : QThread(0x3da9c0)
Start recurse level 3 2
Finished recurce level 3 0
Thread : QThread(0x42b430, name = "Thread (pooled)") : QThread(0x3da9c0)
Start recurse level 3 2
Finished recurce level 3 1
Finished recurce level 3 1
Finished recurce level 3 2
Finished recurce level 3 2Where is my fall?
P.S. I add qDebug for connect call, always return true. Seems like only first recursion deep emitted. But in real example i got also signals for second level (maybe it depends on time of operation). And for last 3-5 calls of second level (all calls second level was about 180-190) I never got finished signal. -
Ok. I I made it work after making QThread object a member and moving each QFutureWatcher object to it. I guess something wrong was in creating watcher in thread of QtConcurrent::run and when one call of recurse with QtConcurrent::run is finished, all watchers created in it, even dynamically and without a parent, were lost, or their connections were lost.
I don't understand why it is so but it works. If someone can explain me this situation and why it works this way, i would be very thankful for explainations, so i would use CONNECT mechanism right in future.