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

Worker/Controller multi-threading and interface class



  • Implemented as it seems to me an interface class for multithreaded objects. Everything works, except this line, when the window is closed.

    connect(_controller, &IClass::finished, this, [] { qDebug() << "finished"; });
    
    1. Do I reinvent the wheel?
    2. Why is the Controller::finished signal not emitted when the object is deleted (when the window is closed)?
    3. Is it possible to make signals private not to create a Private Controller?
    4. Do I need to make the controller a descendant of IClass?
    5. Tips and suggestions

    code example:
    #mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QDebug>
    #include <QMainWindow>
    #include <QThread>
    class Worker1;
    class Worker2;
    
    
    class IClass : public QObject{
        Q_OBJECT
    protected:
        IClass(QObject *parent = nullptr) : QObject(parent) { }
    public:
        enum class Types { Worker1, Worker2, };
        static IClass* CreateInstance(Types t, QObject *parent = nullptr);
    signals:
        void started();
        void finished();
    public slots:
        virtual void f1() = 0;
        virtual void f2(int i) = 0;
        virtual void f3(int x, double y) = 0;
        virtual void start() = 0;
        virtual void stop() = 0;
    };
    
    class PrivateController : public QObject {
        Q_OBJECT
        IClass *_w;
    public:
        PrivateController(IClass *c, QObject *parent = nullptr) : QObject(parent), _w(c) {
            connect(this, &PrivateController::f1, _w, &IClass::f1);
            connect(this, &PrivateController::f2, _w, &IClass::f2);
            connect(this, &PrivateController::f3, _w, &IClass::f3);
            connect(this, &PrivateController::stop, _w, &IClass::stop);
        }
        ~PrivateController() override;
        void callF1()                   { emit f1(); }
        void callF2(int i)              { emit f2(i); }
        void callF3(int x, double y)    { emit f3(x, y); }
        void callStop()                 { emit stop(); }
    signals:
        void f1();
        void f2(int i);
        void f3(int x, double y);
    
        
        void stop();
    };
    
    class Controller : public IClass {
        Q_OBJECT
    public:
        Controller(IClass::Types t, QObject *parent = nullptr)
                            : IClass(parent), _w(CreateInstance(t)), _signals(_w) {
            connect(_w, &IClass::started, this, &IClass::started);
            connect(_w, &IClass::finished, this, &IClass::finished);
        }
        ~Controller() override;
    public slots:
        void f1() override                  { _signals.f1(); }
        void f2(int i) override             { _signals.f2(i); }
        void f3(int x, double y) override   { _signals.f3(x, y); }
        void start() override {
            QThread *th = new QThread;
            _w->moveToThread(th);
            connect(th, &QThread::started, _w, &IClass::start);
            connect(_w, &IClass::finished, th, &QThread::quit);
            connect(_w, &IClass::finished, th, &QThread::deleteLater);
            connect(_w, &IClass::finished, _w, &IClass::deleteLater);
            th->start();
            qDebug() << "Controller::start";
        }
        void stop() override {
            _signals.stop();
            qDebug() << "Controller::stop";
        }
    protected:
        IClass *_w;
        PrivateController _signals;
    };
    
    class Worker1 : public IClass {
        Q_OBJECT
    public:
        Worker1(QObject *parent = nullptr)
                            : IClass(parent) {
            // some code
        }
    public slots:
        void f1() override {
            qDebug() << "Worker1::f1";
        }
        void f2(int i) override {
            qDebug() << "Worker1::f2" << i;
        }
        void f3(int x, double y) override {
            qDebug() << "Worker1::f3" << x << y;
        }
        void start() override {
            emit started();
            qDebug() << "Worker1::start";
        }
        void stop() override {
            emit finished();
            qDebug() << "Worker1::stop";
        }
    protected:
        // some code
    };
    
    class Worker2 : public IClass {
        Q_OBJECT
    public:
        Worker2(QObject *parent = nullptr)
                            : IClass(parent) {
            // some code
        }
    public slots:
        void f1() override {
            qDebug() << "Worker2::f1";
        }
        void f2(int i) override {
            qDebug() << "Worker2::f2" << i;
        }
        void f3(int x, double y) override {
            qDebug() << "Worker2::f3" << x << y;
        }
        void start() override {
            emit started();
            qDebug() << "Worker2::start";
        }
        void stop() override {
            emit finished();
            qDebug() << "Worker2::stop";
        }
    protected:
        // some code
    };
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private:
        Ui::MainWindow *ui;
        IClass *_controller;
    };
    
    #endif // MAINWINDOW_H
    

    #mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow),
        _controller(new Controller(IClass::Types::Worker2, this))
    {
        ui->setupUi(this);
        connect(_controller, &IClass::started, this, [] { qDebug() << "started"; });
        connect(_controller, &IClass::finished, this, [] { qDebug() << "finished"; });
        _controller->start();
        _controller->f1();
        _controller->f2(2);
        _controller->f3(3, 4.4);
    //    _controller->stop();
    }
    
    MainWindow::~MainWindow() { delete ui; }
    
    PrivateController::~PrivateController() { qDebug() << "~PrivateController()"; }
    
    IClass *IClass::CreateInstance(IClass::Types t, QObject *parent) {
        switch(t) {
        case Types::Worker1: { return new Worker1(parent); }
        case Types::Worker2: { return new Worker2(parent); }
        }
    }
    
    Controller::~Controller() { _signals.callStop(); }
    

    OUTPUT:

    Controller::start
    Worker2::start
    Worker2::f1
    Worker2::f2 2
    Worker2::f3 3 4.4
    started
    ~PrivateController()
    Worker2::stop
    

    Don't emited finished.


  • Lifetime Qt Champion

    Hi,

    Unless I missed it, you don't emit the finished signal anywhere.



  • @fryn3 said in Worker/Controller multi-threading and interface class:

    connect(_controller, &IClass::finished, this, [] { qDebug() << "finished"; });

    what @SGaist said, but also...my line of thinking would be to test that the lambda is ever executed. during testing my initial lambda body would be { assert(0); }

    That way you'll know for sure if the lambda is ever executed.



  • @kent-dorfman I tried. The lambda is not executed.


Log in to reply