If you stop a QTimer, will it emit a timeout() signal? - c++

I'm trying to understand the operation of a QTimer. I have things triggered off timeout() signals, but I am unable to find in the documentation if a timeout() signal is emitted if I stop the timer early.
Basically, how can I force a timeout() before the timer finishes counting? Just hack by restarting the timer with the minimum ms increment?
myTimer->start(1);
http://qt-project.org/doc/qt-5/qtimer.html#timeout

In neither Qt 4 nor Qt 5 can you directly emit QTimer::timeout from outside of the class. It's a private signal: in Qt 4, it's declared as private, in Qt 5, it's declared with an argument of a private type QObjectPrivate.
You can invoke it, though:
// fast, guaranteed to work in Qt 4 and 5
myTimer->QTimer::qt_metacall(QMetaObject::InvokeMetaMethod, 5, {});
// slower, has to look up the method by name
QMetaObject::invokeMethod(myTimer, "timeout");
In Qt 5, the moc-generated QTimer::qt_static_metacall constructs the private argument for us:
//...
case 0: _t->timeout(QPrivateSignal()); break;
You can also make the timer act as if it had timed out by sending it a timer event:
void emitTimeout(QTimer * timer) {
Q_ASSERT(timer);
QTimerEvent event{timer->timerId()};
QCoreApplication::sendEvent(timer, &event);
}
Both methods work on both Qt 4 and Qt 5.
Since you're looking to emit a timeout on stopping an active timer, the solutions would be, respectively:
void emitTimeoutAndStop(QTimer * timer) {
Q_ASSERT(timer);
Q_ASSERT(QAbstractEventDispatcher::instance()); // event loop must be on the stack
if (!timer->isActive()) return;
timer->QTimer::qt_metacall(QMetaObject::InvokeMetaMethod, 5, {});
timer->stop();
}
or
void emitTimeoutAndStop(QTimer * timer) {
Q_ASSERT(timer);
Q_ASSERT(QAbstractEventDispatcher::instance()); // event loop must be on the stack
if (!timer->isActive()) return;
QTimerEvent event{timer->timerId()};
QCoreApplication::sendEvent(timer, &event);
timer->stop();
}
The signals will be emitted immediately, and not by Qt code from the event loop. This shouldn't be a problem, since emitTimeoutAndStop will be invoked with an event loop on the stack. We assert that fact. If you wish to support invoking emitTimeoutAndStop from code tied to the same timer's timeout signal without reentering said code, then you have to use the ChattyTimer below, or the solution from another answer.
If all you need isn't quite a timer, but just an immediate, single-emission signal, QObject::destroyed is useful for that purpose. It will be emitted at the end of the block, where source goes out of scope and is destructed.
{ QObject source;
connect(&source, &QObject::destroyed, ...); }
Alternatively, you can have a small helper class:
// signalsource.h
#pragma once
#include <QObject>
class SignalSource : public QObject {
Q_OBJECT
public:
Q_SIGNAL void signal();
SignalSource(QObject * parent = {}) : QObject(parent) {}
};
Since you can connect multiple signals to a single receiver, perhaps it would make things clearer to connect both a timer and such a signal source to the receiver, instead of trying to hack around timer's behavior.
On the other hand, if such "signaling upon stopping" timer is something you find useful in several places, it'd be better to actually implement it as a dedicated class - it's not all that hard. Re-using the QTimer class is not possible, since the stop() slot is not virtual, and thus ChattyTimer is not Liskov-substitutable for a QTimer. This would be a bug waiting to happen - in the class of bugs that are hard to find.
There are several behavioral details that demand attention. This perhaps indicates that changing the behavior of something as fundamental as a timer is tricky - you never know what code might make assumptions that are obviously correct in QTimer, but not so when stop() might emit a timeout. It is a good idea to have all this in a class that is not-a QTimer - it really is not!
As in QTimer, the timeout event is always emitted from the event loop. To have it emitted immediately from stop(), set immediateStopTimeout.
There are two generalizations possible of isActive behavior upon stop (vs. that of QTimer):
becomes false immediately after stop returns, even if the final timeout will be emitted later, or
indicates whether timeout events may be emitted by the event loop, and will remain true after a stop() if the final timeout signal is deferred.
I chose the first behavior to be the default. Set activeUntilLastTimeout to select the second behavior.
There are three generalizations possible of the behavior of each of timerId and remainingTime upon stop (vs. that of QTimer):
returns -1 when isActive() is false, or a valid identifier/time otherwise (i.e. follows chosen isActive() behavior),
becomes -1 immediately after stop returns, even if the final timeout will be emitted later,
returns a valid id/time whenever timeout events may still be emitted by the event loop.
I chose the first behavior for both timerId and remainingTime, and it is not otherwise configurable.
// https://github.com/KubaO/stackoverflown/tree/master/questions/chattytimer-25695203
// chattytimer.h
#pragma once
#include <QAbstractEventDispatcher>
#include <QBasicTimer>
#include <QTimerEvent>
class ChattyTimer : public QObject {
Q_OBJECT
Q_PROPERTY(bool active READ isActive)
Q_PROPERTY(int remainingTime READ remainingTime)
Q_PROPERTY(int interval READ interval WRITE setInterval)
Q_PROPERTY(bool singleShot READ singleShot WRITE setSingleShot)
Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType)
Q_PROPERTY(bool immediateStopTimeout READ immediateStopTimeout WRITE setImmediateStopTimeout)
Q_PROPERTY(bool activeUntilLastTimeout READ activeUntilLastTimeout WRITE setActiveUntilLastTimeout)
Qt::TimerType m_type = Qt::CoarseTimer;
bool m_singleShot = false;
bool m_stopTimeout = false;
bool m_immediateStopTimeout = false;
bool m_activeUntilLastTimeout = false;
QBasicTimer m_timer;
int m_interval = 0;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() != m_timer.timerId()) return;
if (m_singleShot || m_stopTimeout) m_timer.stop();
m_stopTimeout = false;
emit timeout({});
}
public:
ChattyTimer(QObject * parent = {}) : QObject(parent) {}
Q_SLOT void start(int msec) {
m_interval = msec;
start();
}
Q_SLOT void start() {
m_stopTimeout = false;
m_timer.stop(); // don't emit the signal here
m_timer.start(m_interval, m_type, this);
}
Q_SLOT void stop() {
if (!isActive()) return;
m_timer.stop();
m_stopTimeout = !m_immediateStopTimeout;
if (m_immediateStopTimeout)
emit timeout({});
else // defer to the event loop
m_timer.start(0, this);
}
Q_SIGNAL void timeout(QPrivateSignal);
int timerId() const {
return isActive() ? m_timer.timerId() : -1;
}
bool isActive() const {
return m_timer.isActive() && (m_activeUntilLastTimeout || !m_stopTimeout);
}
int remainingTime() const {
return
isActive()
? QAbstractEventDispatcher::instance()->remainingTime(m_timer.timerId())
: -1;
}
int interval() const { return m_interval; }
void setInterval(int msec) {
m_interval = msec;
if (!isActive()) return;
m_timer.stop(); // don't emit the signal here
start();
}
bool singleShot() const { return m_singleShot; }
void setSingleShot(bool s) { m_singleShot = s; }
Qt::TimerType timerType() const { return m_type; }
void setTimerType(Qt::TimerType t) { m_type = t; }
bool immediateStopTimeout() const { return m_immediateStopTimeout; }
void setImmediateStopTimeout(bool s) { m_immediateStopTimeout = s; }
bool activeUntilLastTimeout() const { return m_activeUntilLastTimeout; }
void setActiveUntilLastTimeout(bool s) { m_activeUntilLastTimeout = s; }
};

If you stop a QTimer, will it emit a timeout() signal?
No.
Basically, how can I force a timeout() before the timer finishes
counting? Just hack by restarting the timer with the minimum ms
increment?
Call stop() on the timer, and then cause the signal to be emitted yourself. You could do this by subclassing QTimer and calling a method in your QTimer subclass that emits the signal:
void MyQTimer :: EmitTimeoutSignal() {emit timeout();}
… but if you don't want to go to the bother of making a subclass, an easier approach would be to add a signal to your own class and connect that signal to the QTimer object's timeout() signal (do this only once of course):
connect(this, SIGNAL(MyTimeoutSignal()), myTimer, SIGNAL(timeout()));
… and then your stop-and-fire method would could be done like this:
myTimer->stop();
emit MyTimeoutSignal();

It's actually quite easy to do this, at least in 4.8 and later (not sure about earlier versions): simply setInterval(0) (much like you suggest in your question, although there's no need to stop the timer and restart it).
This app will immediately print "Timer expired" and exit:
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
QTimer timer;
timer.setSingleShot(true);
timer.setInterval(1000 * 60 * 60); // one hour
QObject::connect(
&timer, &QTimer::timeout,
[&]()
{
std::cout << "Timer expired" << std::endl;
app.exit();
});
QTimer::singleShot(
0, //trigger immediately once QtEventLoop is running
[&]()
{
timer.start();
timer.setInterval(0); // Comment this out to run for an hour.
});
app.exec();
}

Related

Qt Thread with ping operation in Linux

I am trying to accomplish what looks like a very simple task... but it's being a nightmare instead.
I developed an app for a Linux Laptop. Inside the app, I would like to have an independent thread that continuously pings another PC (eg once every 5 seconds, forever... well, as long as the laptop is switched on).
Of course, when the PC that the app is pinging is not connected, the app has to work smoothly, without waiting for the ping operation to return... How can I achieve this?
At first I used a QTimer with QProcess:execute("ping"...), which works fine. The problem is that if the other PC doesn't reply, my whole app and its GUI freeze for about one second at each ping operation. Changing the "ping" options (setting "-i0.2", for example), in order to reduce waiting time for the ping operation, didn't help: when the other PC is not connected my app becomes very slow. If I remove the ping, of course everything works smoothly.
So, I decided to insert the "ping" operation in a QThread, but when I try to follow the second example in http://doc.qt.io/qt-4.8/qthread.html, nothing seems to work: the app doesn't even start.
Here's the code:
//Pinger.h
class Pinger : public QThread
{
Q_OBJECT
void run();
public:
void setParam(const QString &urlToPing); // it sets the url to ping
signals:
/// \brief Signal emitted when pinging of specified url fails
void pingFailed(int ok);
private:
QString pingurl;
};
//Pinger.cpp
void Pinger::run()
{
int exitCode;
QProcess pingProc;
while(true)
{
exitCode=pingProc.execute("ping",QStringList() << "-c 1" << "-i0.2" << pingurl);
emit pingFailed(exitCode);
sleep(5);
}
}
// MainWindow::MainWindow
pinga= new Pinger(); // defined in mainwindow.h as Pinger* Pinga
pinga->setParam(ip_address);
connect(pinga,SIGNAL(pingFailed(int)),this,SLOT(connectionLost(int)));
connect(pinga,SIGNAL(finished()),pinga,SLOT(deleteLater()));
pinga->start();
Has anyone tried anything similar? I am quite new to Qt, but this operation seems so trivial that I find it incredible that there's no clear way to implement it. so I hope I am just missing something obvious.
If QThread is used it is better to avoid sleep(5); and the loop while(true), since the thread cannot be closed gracefully without killing it. Instead of the loop and blocking sleep it is better to call that periodic task again by single shot timer initiated when the previous task (process execution) is finished. However, in that case that logic should be implemented in some other member slot (Pinger::doWork()). The slot run() should be kept with its default implementation that executes the tread event loop. The work can be started by connecting the QThread::started() signal with Pinger::doWork():
connect(pinga, SIGNAL(started()), pinga, SLOT(doWork()));
It is needed to be careful with QThread deletion. In general it is not good to delete QThread object by itself (calling deleteLater() from its finished() signal). It is better to stop the thread and to delete it in MainWindow destructor:
MainWindow::~MainWindow
{
// stop the even loop
pinga->quit();
// wait for finishing current thread task; it can work only
// if the thread is not blocked by while(true) with sleep
pinga->wait();
// delete if it is not a smart pointer
delete pinga;
}
It is also possible to use QProcess with its non-blocking API in the main GUI thread without QThread. In that case it should be started by QProcess::start() and the slots connected to the signals QProcess::error() and QProcess::finished() should be used to start the next iteration. Those slots also should not block the main thread, so the next ping should be started using QTimer once previous ping is finished.
Here is an example of the "Qt Way" to write your Pinger class. Note that no threads are needed. QProcess is used asynchronously, and reports its status through a Qt signal. Once you really grok Qt, you'll realize that using threads is very rarely the right or most natural solution to these types of problems.
Note that I'm using Qt 5 with C++11 support enabled to connect Qt signals to C++11 lambdas... you could just as easily write it in Qt4 style but it wouldn't be as compact and readable.
class Pinger : public QObject
{
Q_OBJECT
public:
Pinger(QObject *parent = 0) : QObject(parent)
{
//Have to do this ugliness because QProcess::finished is overloaded
auto finishedFunc = static_cast<void(QProcess::*)(int)>(&QProcess::finished);
connect(&m_process, finishedFunc, [this](int exitCode)
{
if( exitCode == 0 )
{
emit pingSuccess();
}
else
{
emit pingFailed(exitCode);
}
});
}
void run(const QString& hostToPing, int intervalInSeconds)
{
m_host = hostToPing;
QTimer* timer = new QTimer(this);
timer->start(intervalInSeconds * 1000);
QObject::connect(timer, &QTimer::timeout, [this]()
{
if ( m_process.state() == QProcess::NotRunning )
{
m_process.start(QString("ping -c 1 -W 1 %1").arg(m_host));
}
else
{
qDebug() << "Cannot ping, previous ping operation still in progress!";
}
});
}
signals:
void pingSuccess();
void pingFailed(int exitCode);
private:
QProcess m_process;
QString m_host;
};
Using it is as simple as:
Pinger pinger;
QObject::connect(&pinger, &Pinger::pingSuccess, []()
{
qDebug() << "Host is up!";
});
QObject::connect(&pinger, &Pinger::pingFailed, [](int exitCode)
{
qDebug() << "Host is unreachable! Ping exit code = " << exitCode;
});
pinger.run("google.com", 3);
I had the same problem and fixed it by running the ping command in an own thread. By using a signal, I'm interacting with the non-blocking GUI.
1) I've declared a Ping class, which run the Ping command:
class Ping {
public:
static bool start(QString host) {
QStringList parameters;
#if defined(WIN32)
parameters << "-n" << "1";
#else
parameters << "-c 1";
#endif
parameters << host;
int exitCode = QProcess::execute("ping", parameters);
if (exitCode==0) {
return true;
} else {
return false;
}
}
};
2.) Additionally I've a NetworkReceiver class, which creates the Network Thread via a Slot (startConnectionCheck) and interacts with the GUI via a Signal (newConnectionStatus):
class NetworkReceiver : public QObject
{
Q_OBJECT
public:
explicit NetworkReceiver(QObject * parent = nullptr);
~NetworkReceiver();
void start() {
while(true) {
emit newConnectionStatus(Ping::start("google.at"));
QThread::sleep(5);
}
}
signals:
void newConnectionStatus(bool connected);
public slots:
void startConnectionCheck() {
QFuture<void> test = QtConcurrent::run(this, &NetworkReceiver::start);
}
};

Signal from Main Thread not reaching slot in Second Thread Qt 5

I am writing a program that captures an Image from the Camera every REFRESH_RATE milliseconds to send it to some calculation algorithm. I decided to time the launch of this algorithm using a QThread, but the algorithm is running on the main thread, because I need objects created before in this one.
Anyway, this works well.
The trouble begins when I try to shut the second thread down... Here's the code.
Camera:
class Camera : public QMainWindow
{
Q_OBJECT
public:
Camera(QWidget *parent = 0){
AlgoQThread = new QThread;
myAlgoThreadWorker = new AlgoThreadWorker( (Camera *) this );
myAlgoThreadWorker->moveToThread(AlgoQThread);
//Launch The thread
connect(AlgoQThread, SIGNAL(started()), myAlgoThreadWorker, SLOT(process()));
connect(myAlgoThreadWorker, SIGNAL(finished()), AlgoQThread, SLOT(quit()));
// The line meant to stop the thread at next loop
connect(this, SIGNAL(stopAlgoThread()), myAlgoThreadWorker, SLOT(stopThread()));
connect(myAlgoThreadWorker, SIGNAL(finished()), myAlgoThreadWorker, SLOT(deleteLater()));
connect(AlgoQThread, SIGNAL(finished()), AlgoQThread, SLOT(deleteLater()));
AlgoQThread->start();
}
private slots:
void errorString(QString error);
//This function is triggered by a signal sent by the method called in AlgoThreadWoker::process()
void some_algorithm(){
...
emit stopAlgoThread();
}
signals:
void stopAlgoThread();
private:
QThread * AlgoQThread;
AlgoThreadWorker * myAlgoThreadWorker;
};
algoThreadWorker:
class AlgoThreadWorker : public QObject
{
Q_OBJECT
public:
AlgoThreadWorker(Camera * context){
parentCamera = context;
}
Camera* parentCamera;
public slots:
void process(){
while(1){
QMutexLocker locker(&m_mutex);
if (t_stop) break;
parentCamera->isCapturingImage = true;
//This triggers the some_algorithm() using a signal sent by imageCapture from main Thread
parentCamera->imageCapture->capture();
//Wait REFRESH_RATE millisecondes
Sleep(REFRESH_RATE);
}
//Ends the thread
emit finished();
}
private slots:
void stopThread(){
QMutexLocker locker(&m_mutex);
t_stop = true;
};
signals:
void finished();
void error(QString);
private:
bool t_stop;
QMutex m_mutex;
};
And well, as you may have foresee, it doesn't work. I can launch some_algorithm() with no problems but I can't end the thread. The stopThread() slot isn't even launched, I've tested that already.
Since you are busy waiting in your while loop, the event loop never gets a chance to process the received signals.
qApp->processEvents() gives the control to your event loop in order to process the awaiting jobs.
Note that generally you do not have to call it yourself, Qt does it for you. In your case, it is neccessary because you have an endless loop which prevents Qt from doing its job.
Solution :
void process(){
while(1){
qApp->processEvents();
^^^^^^^^^^^^^^^^^^^^^^
.....
}
//Ends the thread
emit finished();
}
stopThread() is private and thus only accessible from AlgoThreadWorker.
Qt's event loop already provides for safe cross-thread slot call and event delivery. Your mistakes are mostly to do with incorrectly reimplementing what Qt already provides:
Things are already thread-safe. Drop the mutex.
thread->quit() works and will exit from the event loop running in the given thread. In a QThread, the thread will then finish,.
Don't make your code non-portable for no reason (Sleep is Windows-specific). If you wish do things periodically, simply run a timer (QBasicTimer or QTimer).
This answer provides a complete example.

How to stop looping thread

I want to stop a looping thread when a signal was emitted so here is my code
void MyThread::stopWatchingThread()
{
qDebug()<<"MyThread::stopWatchingThread()";
Keep_running=false;
qDebug()<<"MyThread::stopWatchingThread Keep_running"<<Keep_running;
...
}
void MyThread::run()
{
qDebug()<<"MyThread::run()";
qDebug()<<"MyThread::run Keep_running"<<Keep_running;
while(Keep_running)
{
...
}
qDebug()<<"MyThread::run Keep_running"<<Keep_running;
Keep_running=false;
qDebug()<<"MyThread::run Keep_running"<<Keep_running;
}
void Watcher::Init()
{
WatchingThread=new MyThread(this->L_RootToWatch);
connect(this,SIGNAL(stopmonotiring()),WatchingThread, SLOT(stopWatchingThread()));
...
}
void Watcher::StartWatching()
{
WatchingThread->start();
}
void Watcher::StopWatching()
{
emit stopmonotiring();
}
So every thing goes all right but my problem is that Keep_running never get false value in MyThread::run() after emitting stopWatchingThread and so while loop for ever.
What did I miss ?
any help will be appreciated.
Don't create threaded classes explicitly in Qt. Instead, create a worker object, move that object to a QThread, then call start() on the QThread. Here's a quick example:
class Worker : public QObject
{
Q_OBJECT
public:
Worker( QObject * parent = 0 )
: QObject( parent )
{}
public slots:
void doWork( ... )
{
// do work here
}
void stopMonitoring()
{
emit finished();
}
signals:
void finished();
};
int main()
{
Worker * w = new Worker();
QThread * thread = new QThread();
QObject::connect( w, SIGNAL(finished()), thread, SLOT(quit())
QObject::connect( w, SIGNAL(finished()), w, SLOT(deleteLater())
QObject::connect( thread, SIGNAL(finished()), thread, SLOT(deleteLater())
w->moveToThread( thread );
thread->start();
// some other object emits a signal connected to the 'doWork()' slot.
}
I omitted some of the standard QApplication boiler-plate, but you have that already if you're using Qt. This should get you started.
As your run() method is blocking and the event loop never entered, the slot stopWatchingThread will never be called. You must call exec() and not block the event loop by a spinning loop in run(). Either that, or have the watcher thread call stopWatchingThread directly instead of using a signal/slot connection. I'd go for the latter. keepRunning will be accessed from multiple threads then, so you have to protect it using a QMutex, QReadWriteLock or QAtomic. (Start with QMutex, it's easiest).
If you use an event loop in your thread just post the quit() signal to the thread object.
Maybe your C++ compiler optimizes away the read operation on Keep_running. Try declaring it as volatile, which tells the compiler that this variable might change "unexpectedly", e.g. from other threads or hardware interrupts.

How to Stop a QThread That Runs a Blocking Forever Loop?

I have some rough code that I've been experimenting with:
someserver.cpp (a GUI)
#include "server.h"
#include "ui_server.h"
Server::Server(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Server)
{
ui->setupUi(this);
}
Server::~Server()
{
delete ui;
}
void Server::onBtnStartClicked()
{
QThread worker;
worker.start(); // Start worker thread that goes into an infinite loop with a blocking call
}
void Server::onBtnExitClicked()
{
// How do I cleanly stop worker from running?
QApplication::quit();
}
worker.cpp
#include "worker.h"
Worker::Worker(QObject *parent) :
QThread(parent)
{
}
void Worker::run()
{
for (;;)
{
// a blocking IO call here like pipe, or msgrcv
// process data received
}
}
Since the worker thread runs in a forever loop with a blocking IO call, how will I be able to structure this so that when the Stop button is pressed in the GUI thread, the worker thread is stopped cleanly?
You could of course put a boolean value within the for loop in Worker::run() checked every iteration, which breaks on ==true and is set by the gui Stop button. Of course, this won't quit the thread while execution is blocked.
Probably better is to get rid of the for loop and use Qt's signals and slots to setup a callback function, connected to a signal like QIODevice::readyRead(). These will be called only when there is information available in the socket/pipe whatever. Any other time you'll be able to quit the thread with QThread::exit(). You'll need to call QThread::exec() at some point as well to get the event loop going.
in infinity loop paste this code and enjoy...
QTime dieTime = QTime::currentTime().addMSecs(1);
while( QTime::currentTime() < dieTime ) {
QCoreApplication::processEvents( QEventLoop::AllEvents, 1);
}
don't forget
#include <QCoreApplication>
#include <QTime>
see you
To stop the worker thread it is necessary to add "stop requested" boolean flag, which should be tested at the beginning of each iteration. Also the synchronization is needed to read/write value of this boolean flag.
Here is the draft version:
class Worker : public QThread {
public:
Worker() : stopRequested(false) {}
void requestStop()
{
QMutexLocker locker(&mutex);
stopRequested = true;
}
void run()
{
forever
{
if (stopRequested())
break;
// a blocking IO call here like pipe, or msgrcv
// process data received
}
}
private:
bool stopRequested()
{
QMutexLocker locker(&mutex);
return stopRequested;
}
QMutex mutex;
bool stopRequested;
};
First of all, you should set the parent for Worker to make it a child of another QObject, to clean it right way when the app is done, and create it on heap. i.e. dynamic way .
Second, simplest way to do what you want is to define slot which will set boolean member of Worker object and check it in each cycle to break endless loop. Don't forget to connect this slot to a right signal.
so you can put in constructor of the Server along the lines
QThread * worker = new QThread (this);
connect (this, SIGNAL(terminator()), worker, SLOT(terminate()));
in Server class declaration:
signals:
terminator();
in class Worker :
private:
bool terminate_; // don't forget initialize it to false
public slots:
void terminate(){terminate_ = true;}
and in the cycle:
if (terminate_) {/* .. do exit things...*/; return;
after that emit signal terminator() from server whenever you need.
If you will need more complicated management then simple stop, you will probably need to protect your state variable with a mutex.
Here you have the explanation of you're problem, i think. When your application will end, you should do something like this :
MyClass::~MyClass()
{
m_thread->requestStop();
m_thread->wait(); // Force the thread trying to destroy
// an object of type MyClass, to wait
// the end of the execution of m_thread
delete m_thread;
}
wait() is the solution for your synchronisation problem. You could also change requestStop() to :
void requestStop()
{
mutex.lock();
stopRequested = true;
mutex.unlock();
wait();
}
Removing QMutexLocker is important to avoid a Deadlock situation.

Is it possible to implement polling with QThread without subclassing it?

I have a class, which is an abstraction of some device.
class Device
{
public:
...
void Start();
void Stop();
void MsgLoop();
signals:
void sMsgArrived();
}
Start() and Stop() are called from GUI thread. Start() begins new thread, which runs MsgLoop(). It looks like this:
void MsgLoop()
{
forever {
if(SUCCESS == ReadMsg()) //synchronous, non-blocking
{
ProcessMsg(); //quite fast
emit sMsgArrived(); //this signal is connected with a slot in GUI thread
}
}
}
When Stop() is called, program should return from MsgLoop() and stop the thread. How can I implement this with QThread without subclassing it?
Generally you have to decide who will be responsible for managing the thread. Is it the Device or the main window? Or possibly some device manager. In your case the Device should probably manage its own thread, so if you don't want to subclass it, use composition:
class Device : QObject
{
Q_OBJECT
public:
Device(QObject * parent = NULL);
void Start();
void Stop();
private slots:
void MsgLoop();
signals:
void sMsgArrived();
private:
QThread thread;
bool stopThread;
};
Device::Device(QObject * parent) : QObject(parent)
{
moveToThread(&thread);
connect(&thread, SIGNAL(started()), this, SLOT(MsgLoop()));
}
void Device::Start()
{
stopThread = false;
thread.start();
}
void Device::Stop()
{
stopThread = true;
thread.wait(); // if you want synchronous stop
}
void Device::MsgLoop()
{
// your loop
while(!stopThread)
if(SUCCESS == ReadMsg())
{
ProcessMsg();
emit sMsgArrived();
}
QThread::currentThread->quit();
}
NOTE: the thread stopping will only work if ReadMsg really is non-blocking. If you later decide to switch to blocking read (and that would probably be appropriate for most cases), you will have to figure out another way how to stop your thread.
If you look at this link you can see that it is possible to run a method in a separate thread without subclassing a QThread.
However what you are asking is running a message loop forever.
If you follow the given example you can run your loop without subclassing but the QThread object will never enter into its own message loop cause it will never return from your slot. So here is an example but I think it would be a bad design
class Device : public QObject
{
Q_OBJECT
public:
Device(QObject* parent = 0);
~Device();
public Q_SLOTS:
void MsgLoop();
};
QThread* thread = new QThread;
Device* device = new Device;
void Widget::onBtnStartClicked()
{
device->moveToThread(thread);
//This will call start method of Device
connect(thread, SIGNAL(started()), device, SLOT(MsgLoop()));
//This will start the event loop of thread
thread->start();
}
void Widget::onBtnStopClicked()
{
//Tells the thread to exit
thread->exit(0);
}
I am afraid you have to subclass a QThread if you want to run a forever loop.
IMHO you shouldn't. Polling requires being in a forever loop. You must do this in QThread's run function so there is no way to re-implement a function without sub-classing first. Even if you were to try and workaround it with a single shot timer I don't recommend it. You are better off(this is how i like to do it) sub-classing QThread, calling moveToThread(), not call exec() and put a forever loop in run. For an example of this look at the Fortune Blocking Client example from qt. If you don't call moveToThread() on QThread then the QThread object still resides in the GUI main thread and they both share the same event loop (which is bad when using polling functions). Calling moveToThread(QThread) without calling exec() means the QThread will not have an event loop(which is good in your case). Calling exec() would start it's own event loop but is not used for polling schemes and you would leave the run function.