Threading without the headache
I was intrigued by the post http://labs.qt.nokia.com/2010/06/17/...oing-it-wrong/ and really like that idea of new way of using threads. So I wrote a simple example, which... does not work
Code:
#ifndef THREADs_TEST_H
#define THREADs_TEST_H
#if defined(_WIN32)
#include <windows.h>
#elif defined(__GNUC__)
#include <unistd.h>
#define Sleep(x) sleep((x)/1000)
#endif //_WIN32
#include <iostream>
#include <QThread>
#include <QObject>
#include <QDialog>
#include <QDialogButtonBox>
#include <QBoxLayout>
#include <QPushButton>
{
Q_OBJECT
public:
virtual ~Cycler(){stop();};
signals:
void done();
public slots:
void cycle() {
arbeit = true;
int i = 0;
while(arbeit) {
Sleep(1000);
std::cout << i++ << " " << std::flush;
};
emit done(); };
void stop(){ arbeit = false;};
private:
volatile bool arbeit;
};
{
Q_OBJECT
public:
explicit CyclerDialog
(QWidget* parent
= 0, Qt
::WindowFlags f
= 0) : QDialog(parent, f
){ main_layout->addWidget(buttons);
setLayout(main_layout);
cycl.moveToThread(&thrd);
connect(buttons, SIGNAL(accepted()), &thrd, SLOT(start()));
connect(&thrd, SIGNAL(started()), &cycl, SLOT(cycle()));
connect(buttons, SIGNAL(rejected()), &cycl, SLOT(stop()));
connect(&cycl, SIGNAL(done()), &thrd, SLOT(quit())); };
virtual ~CyclerDialog(){thrd.wait(1000);};
private:
Cycler cycl;
};
#endif // THREADs_TEST_H
Code:
#include <QApplication>
#include "threads_test.h"
int main(int argc, char *argv[])
{
CyclerDialog window;
window.show();
return(app.exec());
}
It is supposed to start printing numbers in std::cout when OK is clicked, and stop when CANCEL is clicked, but apparently stop() slot is never called. WHY????
Re: Threading without the headache
Simple.
Your slot cycle never returns, so the stop slot never be called by eventLoop
Re: Threading without the headache
Indeed. How do I fix that?
Re: Threading without the headache
Use a QTimer and connect a slot to QTimer::timeout signal.
When the thread emits the started signal, call QTimer::start and when you want to stop it, call QTime::stop
Re: Threading without the headache
I came up to another solution, basically to move stop() to CyclerDialog, and initiate Cycler with reference to CyclerDialog::arbeit flag. It seems to work.
Code:
{
Q_OBJECT
public:
Cycler
(volatile bool &stop_flag
) : QObject(), arbeit
(stop_flag
) {};
virtual ~Cycler(){};
signals:
void done();
public slots:
void cycle() {
arbeit = true;
int i = 0;
while(arbeit) {
Sleep(1000);
std::cout << i++ << " " << std::flush;
};
emit done(); };
private:
volatile bool& arbeit;
};
{
Q_OBJECT
public:
explicit CyclerDialog
(QWidget* parent
= 0, Qt
::WindowFlags f
= 0) : QDialog(parent, f
), cycl
(arbeit
){ main_layout->addWidget(buttons);
setLayout(main_layout);
cycl.moveToThread(&thrd);
connect(buttons, SIGNAL(accepted()), &thrd, SLOT(start()));
connect(&thrd, SIGNAL(started()), &cycl, SLOT(cycle()));
connect(buttons, SIGNAL(rejected()), this, SLOT(stop()));
connect(&cycl, SIGNAL(done()), &thrd, SLOT(quit())); };
virtual ~CyclerDialog(){stop(); thrd.wait(1000);};
public slots:
void stop(){arbeit = false;};
private:
volatile bool arbeit;
Cycler cycl;
};
Re: Threading without the headache
In general, Synchronizing threads through a shared variable is not a good idea; you should, at least, protect the access with a mutex.
The problem of your solution is that the eventLoop of the thread cannot elaborate other events because it's blocked by your slot
Re: Threading without the headache
In general you are right, I do not need a thread for the type of problem I made an example for, QTimer would be sufficient in fact.
But I am rather intertested in a situation when the thread is created just to run some heavy work, and I only need a tool to stop it, the flag then is not really shared, Cycler needs only read access to it, so no mutexes required. To ensure that I could probably make a const function in CyclerDialog returning bool, and initiate the Cycler with a const pointer to CyclerDialog.
Re: Threading without the headache
In the cases you descrided you can (and should) use QThreadPool/QRunnable or QtConcurrent.
For an overview read here
Re: Threading without the headache
Don't use sleep use timer as mcosta suggested (in you case this is best solution).
Another solution is to call event loop inside your loop:
And last a bit strange solution is replace loop with queued meta calls.
Code:
void cycle() {
if (arbeit) {
Sleep(1000);
std::cout << i++ << " " << std::flush;
metaObject->invokeMethod(this, "cycle", Qt::QueuedConnection);
} else {
emit done();
}
}