main and background thread example



  • i wanna write a basic thread example. here's the idea:
    i have the main event loop. before calling app.exec(), i create an object which itself creates an object and puts it in a separate thread for running. i need some way to tell the background thread that main thread has finished (to join()), so i have a bool to indicate that.

    however, when the application exits, something goes wrong. i'm running it using visual studio and the debugging session won't stop. i don't understand what's wrong. here's the code:

    #include <QApplication>
    #include <QObject>
    #include <QThread>
    #include <iostream>
    #include <thread>
    #include <QtWidgets/QLabel>
    #include <atomic>
    
    std::atomic<bool> bMainThreadRunning { true };
    
    class ThreadObj {
    public:
    	void work() {
    		while(bMainThreadRunning) {
    			++counter;
    			std::this_thread::sleep_for(std::chrono::seconds(5));
    		}
    	}
    private:
    	int counter { 0 };
    };
    
    class Obj {
    public:
    	void start() {
    		ThreadObj obj;
    		m_thread = std::thread(&ThreadObj::work, &obj);
    	}
    	void onMainThreadFinished() {
    		bMainThreadRunning = true;
    		m_thread.join();
    	}
    private:
    	std::thread m_thread;
    };
    
    int main(int argc, char *argv[])
    {
    	QApplication a(argc, argv);
    	Obj o;
    	o.start();
    
    	QWidget w;
    	w.show();
    
    	auto ret = a.exec();
    	o.onMainThreadFinished();
    
    	return 0;
    }
    

  • Lifetime Qt Champion

    Hi,

    @user4592357 said in main and background thread example:

    void onMainThreadFinished() {
    bMainThreadRunning = true;
    m_thread.join();
    }

    Shouldn't that be bMainThreadRunning = false; ?



  • my bad, that's right, i tried to implement what's in my actual project in a snippet and that's what i got automatically.
    but anyways that's not the result i expect. what i expect is, when the main thread finishes i need the background thread to be notified and finish immediately, but with this code it was for the interval to exit.
    actually in my actual project i implemented that scenario, it's basically the same code but then i had to change the places where all of these objects are created and this is the result i get.


  • Lifetime Qt Champion

    Well, if you thread just started to sleep, you'll have to wait for your 5 seconds before it ends properly.

    Do you really have that kind of loop in your application ? It could be reworked to use e.g. a QTimer in a worker object.



  • i'll try using QTimer instead of this_thread::sleep_for(). but it still blows my mind how my previous implementation worked the way i wanted with this code.

    and is everything else okay with this code (i.e. the use of atomic bool etc.)?



  • i can't figure out how to do that. this is what i have right now:

    #include <QtWidgets/QMainWindow>
    #include <QApplication>
    #include <QObject>
    #include <QThread>
    #include <iostream>
    #include <thread>
    #include <QtWidgets/QLabel>
    #include <atomic>
    #include <QTimer>
    
    std::atomic<bool> bMainThreadRunning { true };
    
    class ThreadObj : public QObject {
    public:
    	ThreadObj() : counter(0) {}
    	void work() {
    		auto timer = new QTimer;
    		//timer.setInterval(3000);
    		connect(timer, &QTimer::timeout, this, &ThreadObj::inc);
    		timer->start(3000);
    	}
    	void inc() { 
    		while(bMainThreadRunning)
    			++counter;
    	}
    private:
    	int counter { 0 };
    };
    
    class Obj {
    public:
    	void start() {
    		ThreadObj obj;
    		m_thread = std::thread(&ThreadObj::work, &obj);
    	}
    	void onMainThreadFinished() {
    		bMainThreadRunning = false;
    		m_thread.join();
    	}
    private:
    	std::thread m_thread;
    };
    
    int main(int argc, char *argv[]) {
    	QApplication a(argc, argv);
    	Obj o;
    	o.start();
    
    	QWidget w;
    	w.show();
    
    	auto ret = a.exec();
    	o.onMainThreadFinished();
    
    	return 0;
    }
    

  • Lifetime Qt Champion

    You forgot you used a std::thread. Why not use QThread since you are using Qt and a worker object ?



  • i did that. what i need is, when in background thread something weird happens, i tell the main thread to show a message box. then if at some time the "good" state of background thread is restored, main thread closes the message box.

    here's what i have now. actually the application works. but at some points it crashes. looking at the crash log i can say that the reason is the background thread but i don't see how my implementation is wrong. i appended the crash log after code.

    class BgThread : public QThread {
    	Q_OBJECT
    public:
    	explicit BgThread (const int &nSeconds, QObject *parent = nullptr) : QThread(parent), interval(1000 * nSeconds) /* to milliseconds */  {}
    
    	void onStopBgThread() { running = false; }
    
    signals:
    	void somethingWentWrong();
            void restoreGoodState();
    
    private slots:
    	void performWork() {
                 if(/* something went wrong */) {
    		emit somethingWentWrong();
    		good_state = false;
    	     } else if(!good_state) { // means going from bad state to good state
    		emit restoreGoodState();
    		good_state = true;
    	     }
            }
    
    private:
    	void run() override {
                QTimer timer;
    	    connect(&timer, SIGNAL(timeout()), this, SLOT(performWork()), Qt::DirectConnection);
    	    timer.start(interval);
    
                exec();
                quit();
                wait();
            }
    
    private:
    	int interval;
            bool running { true };
            bool good_state { true };
    };
    
    class MainApp {
    public:
        void start_bg_thread(const int seconds) {
           	thread = new BgThread { seconds, this };
            connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    	connect(thread, SIGNAL(somethingWentWrong()), SLOT(onSomethingWentWrong()));
            connect(thread, SIGNAL(restoreGoodState()), SLOT(onRestoreGoodState()));
    	thread->start();
        }
    
        void stop_bg_thread() {
    	thread->onStopBgThread();
        }
    
    public slots:
        void onSomethingWentWrong() {
    	if(!msgBox) {
    		msgBox= new QMessageBox(window());
    		msgBox->setIcon(QMessageBox::Warning);
    		msgBox->setWindowTitle("window title");
    		msgBox->setText("message box text");
    		const auto pExitBtn = msgBox->addButton(tr("Exit"), QMessageBox::AcceptRole);
    		connect(pExitBtn, SIGNAL(clicked()), qApp, SLOT(quit()));
    	}
    	msgBox->exec();	
        }
    
        void onRestoreGoodState() {
    	msgBox->close();
        }
    private:
        BgThread *thread { nullptr };
        QMessageBox *msgBox;
    };
    

    this is basically it. and somewhere in the app init process i call

    MainApp app;
    app.start_bg_thread(5);
    

    the crash log is something like this (stacktrace):

    do_system () from /lib64/libc.so.6
    system () from /lib64/libc.so.6
    // call signal handler
    ...
    pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    QThread::msleep(unsigned long) ()
    start_thread () from /lib64/libpthread.so.0
    clone () from /lib64/libc.so.6
    waitpid () from /lib64/libc.so.6
    do_system () from /lib64/libc.so.6
    system () from /lib64/libc.so.6
    // call signal handler
    BgThread::onStopBgThread ()
    MainApp::qt_metacall(QMetaObject::Call, int, void**) ()
    QMetaObject::activate(QObject*, QMetaObject const*, int, void**) ()
    QCoreApplication::exec()
    main ()
    

  • Lifetime Qt Champion

    Did you check the memory used by you application ?



  • @SGaist

    what do you mean by check? and no, how should i?


  • Lifetime Qt Champion

    Using the top command for example. Valgrind etc.



  • @SGaist

    okay, thanks.
    one last question, as you can see, on timer's timeout i execute a function. is it possible stop the timer (so the function won't be executed), and then restart it again?


  • Lifetime Qt Champion



  • @SGaist

    hi again,
    i ran valgrind and it says there's a memory leak on exec() which is in run() overridden method. so what's wrong?



  • @user4592357 said in main and background thread example:

    ThreadObj obj;
    m_thread = std::thread(&ThreadObj::work, &obj);

    obj is allocated on the stack, it will go out of scope and delete itself



  • @VRonin

    hi, thanks for the reply.
    the code has been modified since the first post (see my last code-post), so it's not the problem





  • @VRonin

    i've read similar articles, saying "you shouldn't subclass QThread". in my case i need to do that.


  • Lifetime Qt Champion

    Out of curiosity, what are you doing that requires to subclass QThread ?



  • @SGaist

    nothing requires it. it's just that the whole implementation is done and i don't wanna change everything


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.