Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 890
Kto To Avatar asked Feb 02 '26 09:02

Kto To


1 Answers

About question #2: try this edit to your main:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    int r = a.exec();

    qDebug() << "event loop gone ...";

    return r;
}

Check your output, now. As you can see, when the finished signal is emitted, your main thread hasn't an event loop anymore, so the signal won't be caught (because it's coming from the other thread).

To prove it further, add a push button to the window and have a clicked slot like this:

void MainWindow::on_pushButton_clicked()
{
    _controller->stop();
    qApp->processEvents();
    qApp->exit();
}

This way you exit the app, but only after all queued events got processed (i.e. the queued signal gets delivered ...).

About everything else you ask, frankly: it's seems quite broad even for a bounty question. Anyway, hope I have helped a bit.

like image 102
p-a-o-l-o Avatar answered Feb 05 '26 05:02

p-a-o-l-o



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!