Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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 2

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


Log in to reply