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

stop a loop in object that has been moved to a QThread?



  • Hi
    I currently write a communication app that needs to continously receive SPI data.
    So I have a mainwindow with a start and stop button. I created a worker class which then contains the SPI communication. To not block the mainwindow I created a QThread and moved the worker class object into it.

    I can start the doWork() process sucessfuly but how to stop such a loop? Sending signals to the worker class seems not to work since I guess the eventloop is blocked as long as the doWork() loop runs. How to solve that?

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    #include <QWidget>
    #include <QThread>
    class MainWindow : public QWidget
    {
    	Q_OBJECT
    	QThread thread;
    public:
    	explicit MainWindow(QWidget *parent = nullptr);
    	~MainWindow();
    };
    #endif // MAINWINDOW_H
    

    mainwindow.cpp

    #include "mainwindow.h"
    #include <QPushButton>
    #include "worker.h"
    #include <QThread>
    MainWindow::MainWindow(QWidget *parent)
    	:QWidget(parent)
    {
    	QPushButton *pbStart = new QPushButton("START LOOP",this);
    	QPushButton *pbStop = new QPushButton("STOP LOOP",this);
    	pbStop->move(0,40);
    
    	Worker *worker = new Worker;
    	worker->moveToThread(&thread);
    
    	connect(&thread, &QThread::finished, worker, &Worker::deleteLater);
    	connect(pbStart, &QPushButton::clicked, worker, &Worker::doWork);
    	thread.start();
    }
    
    MainWindow::~MainWindow()
    {
    	thread.quit();
    	thread.wait();
    }
    

    worker.h

    #ifndef WORKER_H
    #define WORKER_H
    #include <QObject>
    class Worker :public QObject
    {
    	Q_OBJECT
    public:
    	Worker();
    	~Worker();
    
    public slots:
    	void doWork();
    };
    #endif // WORKER_H
    

    worker.cpp

    #include "worker.h"
    
    Worker::Worker(){}
    Worker::~Worker(){}
    
    void Worker::doWork()
    {
    	while(true){
    		// SPI loop communication
    	}
    };
    

    Can you give me some hints how to solve that?



  • @pauledd hi,

    take a look on how they use signal/slot to communicate with the worker object https://doc.qt.io/qt-5/qthread.html#details
    and avoid using an infinite loop


  • Lifetime Qt Champion

    Hi,

    If you want to keep the loop then add a variable to break it.



  • @pauledd said in stop a loop in object that has been moved to a QThread?:

    Worker::doWork()

    As shown, at no time is this function checking the Qt Event loop.

    public slots:
        void doWork(){
            ...
            while(m_running){
                // process server
                UA_Server_run_iterate(m_server, true);
    
                // process events like signals
                QCoreApplication::processEvents();
            }
            ...
        }
    
        void stopWork(){
            m_running = false;
        }
    

    There are signals connected to the slots above to control my server.



  • thank you for your hints. I go for @fcarney 's solution for now, but I almost surely will have to gather SPI data in bigger chunks too and send them back to the gui for further processing/storing so that the aquisition loop wont be infinite anyway.


  • Moderators

    @pauledd
    I would actually not recommend that.

    I don't know why people keep on suggesting processEvents, as a legit solution. It drives me up the wall every time I see it.



  • @pauledd
    I would recommend using the build-in QThread mechanism to interrupt a thread:

    public slots:
        void doWork() {
            forever() {
                ...
                if ( QThread::currentThread()->isInterruptionRequested() ) {
                            return;
                }
                ...
            }
        }
    

    You can add this check to your SPI loop multiple times but avoid to call it too often, according to the dokumentation.
    Add something like this to your MainWindow-Instance:

    public slots:
        void cancel() {
            if( thread.isRunning()) {
                thread.requestInterruption();
            }
        }
    

    and connect this Slot to some Cancel-Button.