QThread documentation suggests two ways to make code run in a separate thread. If I subclass QThread and reimplement run(), then I get
QBasicTimer::start: Timers cannot be started from another thread
-
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QThread
{
Q_OBJECT
int id;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == id) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
qDebug() << QThread::currentThreadId();
QBasicTimer timer;
id = timer.timerId();
timer.start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
qDebug() << QThread::currentThreadId();
worker = new Worker;
worker->start();
}
~MainWidget(){worker->abort();}
};
1) Is the timer being started from another thread?
2) Why I don't get that warning when QBasicTimer is replaced by QTimer?
3) Why I don't get that warning when using moveToThread?
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QObject
{
Q_OBJECT
QBasicTimer* timer;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == timer->timerId()) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
timer = new QBasicTimer;
timer->start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
~MainWidget(){worker->abort(); thread.quit(); thread.wait();}
signals:
void start();
};
Regarding the first (non-moveToThread) example...
A quick look at the Qt source for QBasicTimer::start shows the following...
void QBasicTimer::start(int msec, QObject *obj)
{
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
// ...
if (Q_UNLIKELY(obj && obj->thread() != eventDispatcher->thread())) {
qWarning("QBasicTimer::start: Timers cannot be started from another thread");
return;
}
So it expects its second argument obj to have a thread affinity equal to the current thread.
In your Worker::run implementation, however, you have...
timer.start(1000, this);
In this context the current thread is the new thread created by the QThread instance but this refers to the QWorker instance created by the MainWidget on the main GUI thread. Hence the warning.
Edit 1:
To the question...
why it works with moveToThread()?
Consider the implementation of the MainWidget ctor...
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
By the time Worker::run is called the Worker instance has been moved to the new thread. So when the line...
timer.start(1000, this);
executes, this (which refers to the Worker instance) is on the current thread and the thread affinity test in QBasicTimer::start passes without warning.
Sorry if the above is a bit convoluted but the important thing is to consider the thread affinity of the second arg to QBasicTimer::start: it must be the currently running thread.
Related
I created a Worker class for manager threads in Qt.
#include <QThread>
#include <QProcess>
#include <QObject>
class Worker_Bis : public QObject
{
Q_OBJECT
public:
Worker_Bis() : flag_running(false), flag_stopping(false){
thread = new QThread();
moveToThread(thread);
connect(thread, SIGNAL(started()), this, SLOT(start()));
connect(thread, SIGNAL(terminated()), this, SLOT(stop()));
connect(this, SIGNAL(finished()), thread, SLOT(quit()));
connect(this, SIGNAL(terminated()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), this, SLOT(deleteLater()));
thread->start();
}
~Worker_Bis(){
if (!isRunning())
thread->deleteLater();
flag_stopping = true;
while (isRunning()){}
deleteLater();
}
bool isRunning() { return flag_running; };
bool isStopping() { return flag_stopping; };
virtual int getLoops() { return 1; };
public slots:
void start(){
flag_running = true;
flag_stopping = false;
starting();
emit started();
int exit;
for (int loop = 0; loop < getLoops(); ++loop){
if (isStopping()){
exit = -1;
break;
}
emit startLoop(loop);
exit = exec(loop);
if (exit != 0)
break;
emit finishLoop(loop);
}
finishing(exit);
if (exit == -1)
emit terminated();
else{
emit finished();
if (exit != 0)
emit error(exit);
}
flag_running = false;
};
void stop(){
flag_stopping = true;
stopping();
emit stopped();
};
signals:
void started();
void finished();
void stopped();
void terminated();
void error(int code);
void startLoop(int loop);
void finishLoop(int loop);
protected:
QThread* thread;
bool flag_stopping;
bool flag_running;
virtual void starting() {};
virtual int exec(int loop) { return 0; };
virtual void finishing(int) {};
virtual void stopping() {};
};
And I implemented twos buttons: start and stop. I start a thread clicking in start button and I stop thread clicking in stop button.
void ButtonStartClicked(){
worker = new Worker(myObj);
}
void ButtonStopClicked(){
delete worker;
}
If I click in start button many times, I will get parallel threads. Can I obtain some memory problems clicking in start button many times?
I'm trying to create my own worker thread for I/O operations. As far as I know the low-level management of the serial port is already done in a separate thread, but I want to put my code in another one to handle timeouts and other middleware stuff.
Here the complete code of my class. Please note it's my first attempt with Qt's multi-threading.
EDIT
The code is updated without sub-classing QThread
#ifndef WORKERSERIAL_H
#define WORKERSERIAL_H
#include <QObject>
#include <QThread>
#include <QQueue>
#include <QMutex>
#include <QTimer>
#include <QDebug>
#include "myserial.h"
class WorkerSerial : public QObject
{
Q_OBJECT
public:
explicit WorkerSerial(QObject *parent = 0) : QObject(parent)
{
m_serial = new MySerial(this);
connect(m_serial, &MySerial::lineReceived, this, &WorkerSerial::serial_LineReceived);
m_stop = false;
}
bool open(QString port, quint32 baudrate) { return m_serial->open(port, baudrate); }
bool isOpen() { return m_serial->serial()->isOpen(); }
QStringList ports() { return m_serial->ports(); }
private:
MySerial *m_serial;
QQueue<QString> m_queue;
QMutex m_mutex;
bool m_stop;
private slots:
void serial_LineReceived(QByteArray line)
{
emit lineReceived(line);
}
signals:
void lineReceived(QByteArray line);
public slots:
void close() { m_serial->close(); }
void send(QString data) { m_mutex.lock(); m_queue.enqueue(data); m_mutex.unlock(); }
void stop() { m_mutex.lock(); m_stop = true; m_mutex.unlock(); }
void run() {
forever {
m_mutex.lock();
if (m_stop)
{
qDebug() << "Closing...";
QTimer::singleShot(0, this, SLOT(close()));
while(m_serial->serial()->isOpen());
m_mutex.unlock();
return;
}
if (!m_queue.isEmpty())
{
QString data = m_queue.dequeue();
qDebug() << m_serial->sendMessage(data.toLocal8Bit(), true);
}
m_mutex.unlock();
}
}
};
#endif // WORKERSERIAL_H
The MySerial class it's a convenient wrapper for QSerialPort. Here the relevant functions:
bool MySerial::open(QString port, quint32 baudrate) {
m_serial->setPortName(port);
if (!m_serial->open(QIODevice::ReadWrite)) return false;
m_serial->setDataBits(QSerialPort::Data7);
m_serial->setParity(QSerialPort::EvenParity);
m_serial->setStopBits(QSerialPort::TwoStop);
m_serial->setBaudRate(baudrate);
m_serial->setFlowControl(QSerialPort::NoFlowControl);
return true;
}
QByteArray MySerial::sendMessage(QByteArray data) {
m_serial->write(data);
return data;
}
Finally, here how I start, use and close the worker:
QThread *workerThread = new QThread;
m_worker = new WorkerSerial;
m_worker->moveToThread(workerThread);
workerThread->start();
QTimer::singleShot(0, m_workerDMX, SLOT(run()));
// ...
m_worker->open("COM1", 250000));
// ...
m_worker->send("Hello World!");
// ...
m_worker->stop();
workerThread->quit();
workerThread->wait(1000);
// Here the application ends
This is the output:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x19882160), parent's thread is QThread(0x187fe598), current thread is WorkerSerial(0x19d4c9b8)
[this error appears when it calls the SendMessage() function]
"Hello World!"
Closing...
QMutex: destroying locked mutex
QThread: Destroyed while thread is still running
[the two errors above are due the singleSlot() isn't executed and thus the serial won't close]
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
It seems clear I've messed up something!
Would you help me to understand my mistakes?
Here is (an incomplete) implementation, which should give you an idea of how to simplify your code. Some additional notes are below:
class WorkerSerial : public QObject
{
Q_OBJECT
public:
explicit WorkerSerial(QObject *parent = 0) : QObject(parent)
{
// no need for slot -- connect directly to our signal
connect(&m_serial, &MySerial::lineReceived, this, &WorkerSerial::lineReceived);
}
bool open(QString port, quint32 baudrate) { return m_serial.open(port, baudrate); }
bool isOpen() { return m_serial.serial().isOpen(); }
QStringList ports() { return m_serial.ports(); }
private:
// doesn't need to be a pointer
MySerial m_serial;
signals:
void lineReceived(const QByteArray& line);
public slots:
void close() { m_serial.close(); }
void send(const QByteArray& data)
{
// "lengthy" function call
qDebug() << m_serial.sendMessage(data, true);
}
};
class Sender : public QObject
{
Q_OBJECT
public:
void send(const QByteArray& data) { emit sig_send(data); }
signals:
void sig_send(const QByteArray& data);
}
...
WorkerSerial m_worker;
m_worker.open("COM1", 250000));
QThread m_thread;
m_worker.moveToThread(&m_thread);
m_thread.start();
Sender m_sender;
QObject::connect(&m_sender, &Sender::sig_send, &m_worker, &WorkerSerial::send, Qt::QueuedConnection);
m_sender.send("Hello World!");
m_thread.quit();
m_thread.wait();
m_worker.close();
Additional notes:
I've added a Sender class that queues calls to WorkerSerial::send() on a different thread. If you have a signal in your code, which triggers calls to WorkerSerial::send() you don't need this class.
Once you move m_worker to another thread, it is unsafe to access any of its methods, unless (a) they are guaranteed to be thread-safe; or (b) you serialize (protect) access to m_serial with a mutex.
NOTE: this is not a fully working solution (yet). But I think it's better to post it as an answer than add another piece of code in the original question.
I managed to a different approach. Perhaps it looks weird... but it should have some advantages:
it guarantees the order of the messages to be transmitted
using a QQueue I can know the queue size or other information about
the "variable shadowing" mechanism should allow to safely communicate with the thread
Here the code, below the two last issues:
#ifndef WORKERSERIAL_H
#define WORKERSERIAL_H
#include <QObject>
#include <QThread>
#include <QQueue>
#include <QMutex>
#include <QDebug>
#include "myserial.h"
class WorkerSerial : public QThread
{
Q_OBJECT
public:
explicit WorkerSerial(QObject *parent = 0, int timeout = 0) : QThread(parent), timeout(timeout)
{
quit = false;
command = None;
}
~WorkerSerial()
{
mutex.lock();
quit = true;
mutex.unlock();
wait();
}
void open(QString port, quint32 baudrate)
{
mutex.lock();
this->port = port;
this->baudrate = baudrate;
command = Open;
mutex.unlock();
}
void close()
{
mutex.lock();
command = Close;
mutex.unlock();
}
void stop()
{
mutex.lock();
command = Quit;
mutex.unlock();
}
private:
enum Commands
{
None,
Open,
Close,
Quit
};
QQueue<QString> m_queue;
QMutex mutex;
int timeout;
int command;
QString port;
int baudrate;
QString request;
bool quit;
signals:
void portStateChanged(bool isOpen);
void lineReceived(QByteArray line);
public slots:
void send(QString data)
{
mutex.lock();
m_queue.enqueue(data);
mutex.unlock();
}
void run()
{
MySerial serial;
QString _port;
int _baudrate;
int _timeout;
QString _request = "";
int _command = None;
connect(&serial, &MySerial::lineReceived, this, &WorkerSerial::lineReceived);
mutex.lock();
_timeout = timeout;
mutex.unlock();
forever {
mutex.lock();
_command = command;
command = None;
if (!m_queue.isEmpty()) _request = m_queue.dequeue();
mutex.unlock();
switch (_command) {
case Open:
mutex.lock();
_port = port;
_baudrate = baudrate;
mutex.unlock();
if (serial.open(_port, _baudrate)) emit portStateChanged(true);
else portStateChanged(false);
break;
case Close:
serial.close();
emit portStateChanged(serial.serial()->isOpen());
break;
case Quit:
serial.close();
return;
break;
default:
break;
}
if (!_request.isEmpty())
{
qDebug() << serial.sendMessage(_request.toLocal8Bit(), true);
_request = "";
}
msleep(1);
}
}
};
#endif // WORKERSERIAL_H
Here how to use it:
WorkerSerial *worker = new WorkerSerial(this);
connect(worker , &WorkerSerial::lineReceived, this, &MainWindow::lineReceived);
connect(worker , &WorkerSerial::portStateChanged, this, &MainWindow::portStateChanged);
worker->start();
// ..
worker->open("COM2", 250000);
worker->send("Hello World");
// ..
worker->stop();
worker->wait(1000);
It "works" but there are two main issues:
the serialport is opened but the data are not actually transmitted. The MySerial class is working because if I do the following, bypassing the WorkerSerial:
MySerial serial;
serial.open("COM2", 250000);
serial.sendMessage("Hello World!", true);
the data is sent! Hence there is still something wrong in my WorkerSerial.
due to the forever loop, I need to insert a small delay, otherwise the CPU will run up to 100% doing nothing.
I have been trying to get this simple example using threads activated by pushbuttons to work. It is based off of the solution in the question below:
How to implement frequent start/stop of a thread (QThread)
The main differences between the example solution above and my code below are:
I used a QWidget instead of MainWindow
I changed the name of signals for clarity
My code contains debugging information
I experimented with eliminating the signals created by worker as the didn't appear to do anything
It appears that the start/stop signals are not triggering their corresponding slots, but I am not experienced enough to troubleshoot why.
Additionally, I am unsure of the purpose of the signal:
SignalToObj_mainThreadGUI()
Is that just something that could be used and is not?
I have been trying to get this code to work for some time, so any help would be greatly appreciated.
main.cpp
#include "threadtest.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ThreadTest w;
w.show();
return a.exec();
}
threadtest.h
#include <QWidget>
#include <QThread>
#include "worker.h"
namespace Ui
{
class ThreadTest;
}
class ThreadTest : public QWidget
{
Q_OBJECT
public:
explicit ThreadTest(QWidget *parent = 0);
~ThreadTest();
signals:
void startWorkSignal();
void stopWorkSignal();
private slots:
void on_startButton_clicked();
void on_stopButton_clicked();
private:
Ui::ThreadTest *ui;
worker *myWorker;
QThread *WorkerThread;
};
threadtest.cpp
#include "threadtest.h"
#include "ui_threadtest.h"
ThreadTest::ThreadTest(QWidget *parent) :
QWidget(parent),
ui(new Ui::ThreadTest)
{
ui->setupUi(this);
myWorker = new worker;
WorkerThread = new QThread;
myWorker->moveToThread(WorkerThread);
connect(this,
SIGNAL(startWorkSignal()),
myWorker,
SLOT(StartWork())
);
connect(this,
SIGNAL(stopWorkSignal()),
myWorker,
SLOT(StopWork())
);
//Debug
this->dumpObjectInfo();
myWorker->dumpObjectInfo();
}
ThreadTest::~ThreadTest()
{
delete ui;
}
void ThreadTest::on_startButton_clicked()
{
qDebug() << "startwork signal emmitted";
emit startWorkSignal();
}
void ThreadTest::on_stopButton_clicked()
{
qDebug() << "stopwork signal emmitted";
emit stopWorkSignal();
}
worker.h
#include <QObject>
#include <QDebug>
class worker : public QObject {
Q_OBJECT
public:
explicit worker(QObject *parent = 0);
~worker();
signals:
void SignalToObj_mainThreadGUI();
//void running();
//void stopped();
public slots:
void StopWork();
void StartWork();
private slots:
void do_Work();
private:
volatile bool running, stopped;
};
worker.cpp
#include "worker.h"
worker::worker(QObject *parent) : QObject(parent), stopped(false),
running(false)
{
qDebug() << "running: " << running;
qDebug() << "stopped: " << stopped;
}
worker::~worker() {}
void worker::do_Work()
{
qDebug() << "inside do Work";
emit SignalToObj_mainThreadGUI();
if (!running || stopped) return;
// actual work here
/*
for (int i = 0; i < 100; i++)
{
qDebug() << "count: " + i;
}
*/
QMetaObject::invokeMethod(this, "do_Work", Qt::QueuedConnection);
}
void worker::StopWork()
{
qDebug() << "inside StopWork";
stopped = true;
running = false;
//emit stopped();
}
void worker::StartWork()
{
qDebug() << "inside StartWork";
stopped = false;
running = true;
//emit running();
do_Work();
}
You should write
WorkerThread->start();
Or you can use the thread of the ThreadTest object instead the WorkerThread (in this case the WorkerThread is needless):
myWorker->moveToThread(thread()); // this->thread
The slots are not triggered, because you have moved myWork to the thread WorkerThread, but didnot run an event loop in that thread. In threadtest.cpp, add
WorkerThread .start();
after
myWorker = new worker;
WorkerThread = new QThread;
myWorker->moveToThread(WorkerThread);
With this program I press the 'run' button and a for loop cycles 100 times (with a 100ms delay)
and prints the cycle count in a txt field
I have successfully done it with a MyThread object derived from QThread. It works. I can
interrupt the cycle with the 'stop' button.
However it is sternly warned that deriving an object from QThread is very bad. So I did it
the other way they suggested, the "right" way.
And it doesn't work. I can get the loop cycle numbers out on the console but not into the text box
The 'run' button goes down and never comes up again until the 100 cycles are done.
And the text field shows 99.
Here is the code, what am I doing wrong?
// MyWidget.h SF022
#ifndef MYWIDGET_H_
#define MYWIDGET_H_
#include <QtWidgets/QWidget>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include "../MyThread.h"
#include "../MyObject.h"
#include <QThread>
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget(QWidget* parent = 0);
~MyWidget();
QPushButton* pbRun;
QPushButton* pbStop;
QPushButton* pbExit;
QLineEdit* txtCount;
QThread* cThread;
MyObject* myObject;
public slots:
void onNumberChanged(int);
private slots:
void pbRun_Slot();
void pbStop_Slot();
void pbExit_Slot();
};
#endif /* MYWIDGET_H_ */
------------------------------------------------
// MyWidget.cpp
#include "MyWidget.h"
#include "../K.h"
#include <iostream>
MyWidget::MyWidget(QWidget* parent) : QWidget(parent) {
pbRun = new QPushButton(this);
pbRun->setObjectName(QStringLiteral("pbRun"));
pbRun->setGeometry(QRect(20, 20, 80, 40));
pbRun->setText("Run");
connect(pbRun, SIGNAL(clicked()), this, SLOT(pbRun_Slot()));
pbStop = new QPushButton(this);
pbStop->setObjectName(QStringLiteral("pbStop"));
pbStop->setGeometry(QRect(20, 80, 80, 40));
pbStop->setText("Stop");
connect(pbStop, SIGNAL(clicked()), this, SLOT(pbStop_Slot()));
pbExit = new QPushButton(this);
pbExit->setObjectName(QStringLiteral("pbExit"));
pbExit->setGeometry(QRect(20, 140, 80, 40));
pbExit->setText("Exit");
connect(pbExit, SIGNAL(clicked()), this, SLOT(pbExit_Slot()));
txtCount = new QLineEdit(this);
txtCount->setGeometry(QRect(20, 200, 80, 40));
txtCount->setStyleSheet("QLineEdit{background: white;}");
// myObject holds the cycling mechanism
myObject = new MyObject(this);
// the myObject sends each new cycle number out here
connect(myObject, SIGNAL(numberChanged(int)), this, SLOT(onNumberChanged(int)));
}
MyWidget::~MyWidget() {
}
void MyWidget::pbRun_Slot() {
// start thread
cThread = new QThread(this);
myObject->doSetup(*cThread);
myObject->moveToThread(cThread);
cThread->start();
}
void MyWidget::pbStop_Slot() {
// stop the thread
myObject->Stop = true;
}
void MyWidget::pbExit_Slot() {
// a static pointer to the main window
(K::SfMainWin)->close();
}
// a slot
void MyWidget::onNumberChanged(int j) {
// output the cycle count to a text field
txtCount->setText(QString::number(j));
}
----------------------------------------------------------
// MyObject.h
#ifndef MYOBJECT_H_
#define MYOBJECT_H_
#include <QObject>
#include <QThread>
class MyObject : public QObject {
Q_OBJECT
public:
explicit MyObject(QObject* parent = 0);
~MyObject();
void doSetup(QThread&);
bool Stop;
signals:
void numberChanged(int);
public slots:
void doWork();
};
#endif /* MYOBJECT_H_ */
----------------------------------------------------------
// MyObject.cpp
#include "MyObject.h"
#include <QMutex>
#include <iostream>
#include "string.h"
MyObject::MyObject(QObject* parent) : QObject(parent) {
Stop = false;
}
MyObject::~MyObject() {
}
void MyObject::doSetup(QThread& cThread) {
Stop = false;
connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}
void MyObject::doWork() {
for (int i = 0; i < 100; i++) {
QMutex mutex;
mutex.lock();
if (this->Stop) {
break;
}
// output into a text field
emit numberChanged(i);
// output on the console
std::cout << "running " << (QString::number(i)).toStdString() << std::endl;
mutex.unlock();
QThread::msleep(100);
}
}
myObject is never moved to the thread you created. Everything is being executed in the main thread. Because
myObject = new MyObject(this);
To move a QObject to another thread, he should not have a parent. If it does Qt will silently tell you something went wrong ( by printing on the output, same with incorrect connections). It is the framework design to not panic over this type of warnings...
It should have been
myObject = new MyObject(0);
Now that this is cleared, you have other defects in the code.
QMutex mutex; is local and will always be acquired by the same thread. which means he has no purpose. Instead it should be a private member of MyObject
MyWidget::pbStop_Slot should be a method of MyObject instead, otherwise you have a race condition when accessing Stop member. Remember the mutex above? It is the time to use it. By the way, with your implementation call directly the method, because the even loop of cThread is only executing doWork
MyObject::pbStop_Slot()
{
mutex.lock()
Stop = true;
mutex.unlock()
}
Now your program should be technically correct. But It suck that you cannot use signals and slots because your thread is blocked executing doWork. Also, can we do something about that lock. In fact, yes. The way I will go is to use a Qtimer as a heartbeat ech 100ms rather than asking the thread to sleep. But to not change much your code you can use QAbstractEventDispatcher * QAbstractEventDispatcher::instance ( QThread * thread = 0 ) directly
MyObject::pbStop_Slot() //becomes a real slot again
{
// no more mutex
Stop = true;
}
....
//this connection is changed
connect(pbStop, SIGNAL(clicked()), myObject, SLOT(pbStop_Slot()));
....
void MyObject::doWork() {
for (int i = 0; i < 100; i++) {
//no mutex
if (this->Stop) {
break;
}
// output into a text field
emit numberChanged(i);
// output on the console
std::cout << "running " << (QString::number(i)).toStdString() << std::endl;
//process events, to allow stop to be processed using signals and slots
QAbstractEventDispatcher::instance(cThread)->processEvents();
QThread::msleep(100);
}
}
A warning about processEvents. As it is now, if the user presses run while dowork is being executed it will be called within itself. You have a nasty code right now. An easy way to circumvent this is to put a boolean which is checked and set at the beginning of dowork.
dowork(){
if(isdoingwork)
return;
isdoingwork = true
for(...
This is a poor man way of achieving reentrancy . You will see the word reentrant quite often in Qt documentation.
Good luck in your multithreading trip.
Very good UmNyobe!
For reference I've added the corrected code here.
// MyWidget.h
#ifndef MYWIDGET_H_
#define MYWIDGET_H_
#include <QtWidgets/QWidget>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include "../MyObject.h"
#include <QThread>
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget(QWidget* parent = 0);
~MyWidget();
QPushButton* pbRun;
QPushButton* pbStop;
QPushButton* pbExit;
QLineEdit* txtCount;
QThread* wThread;
MyObject* myObject;
public slots:
void onNumberChanged(int);
private slots:
void pbRun_Slot();
// void pbStop_Slot();
void pbExit_Slot();
};
#endif /* MYWIDGET_H_ */
-----------------------------------------
// MyWidget.cpp
#include "MyWidget.h"
#include "../K.h"
#include <iostream>
MyWidget::MyWidget(QWidget* parent) : QWidget(parent) {
pbRun = new QPushButton(this);
pbRun->setObjectName(QStringLiteral("pbRun"));
pbRun->setGeometry(QRect(20, 20, 80, 40));
pbRun->setText("Run");
connect(pbRun, SIGNAL(clicked()), this, SLOT(pbRun_Slot()));
pbStop = new QPushButton(this);
pbStop->setObjectName(QStringLiteral("pbStop"));
pbStop->setGeometry(QRect(20, 80, 80, 40));
pbStop->setText("Stop");
pbExit = new QPushButton(this);
pbExit->setObjectName(QStringLiteral("pbExit"));
pbExit->setGeometry(QRect(20, 140, 80, 40));
pbExit->setText("Exit");
connect(pbExit, SIGNAL(clicked()), this, SLOT(pbExit_Slot()));
txtCount = new QLineEdit(this);
txtCount->setGeometry(QRect(20, 200, 80, 40));
txtCount->setStyleSheet("QLineEdit{background: white;}");
myObject = new MyObject(0);
connect(myObject, SIGNAL(numberChanged(int)), this, SLOT(onNumberChanged(int)));
connect(pbStop, SIGNAL(clicked()), myObject, SLOT(pbStop_Slot()));
}
MyWidget::~MyWidget() {
delete myObject;
delete wThread;
}
void MyWidget::pbRun_Slot() {
// start QThread*, wThread in the MyWidget class
wThread = new QThread(this);
myObject->doSetup(wThread);
myObject->moveToThread(wThread);
wThread->start();
}
void MyWidget::pbExit_Slot() {
// a static pointer of the main window
(K::SfMainWin)->close();
}
void MyWidget::onNumberChanged(int j) {
txtCount->setText(QString::number(j));
}
---------------------------------------------------------
// MyObject.h
#ifndef MYOBJECT_H_
#define MYOBJECT_H_
#include <QObject>
#include <QThread>
#include <QMutex>
class MyObject : public QObject {
Q_OBJECT
public:
explicit MyObject(QObject* parent = 0);
~MyObject();
void doSetup(QThread*);
int hold;
bool Stop;
int inx;
int lastUsedInx;
signals:
void numberChanged(int);
public slots:
void doWork();
void pbStop_Slot();
private:
bool isdoingwork;
QThread* pThread;
QMutex mutex;
};
#endif /* MYOBJECT_H_ */
----------------------------------------------------
// MyObject.cpp SF022
#include "MyObject.h"
#include <iostream>
#include <QAbstractEventDispatcher>
MyObject::MyObject(QObject* parent) : QObject(parent) {
Stop = false;
isdoingwork = false;
inx = 0;
lastUsedInx = 0;
}
MyObject::~MyObject() {
}
void MyObject::doSetup(QThread* thread) {
pThread = thread;
Stop = false;
isdoingwork = false;
connect(pThread, SIGNAL(started()), this, SLOT(doWork()));
}
void MyObject::pbStop_Slot() {
mutex.lock();
Stop = true;
isdoingwork = false;
mutex.unlock();
}
void MyObject::doWork() {
if(isdoingwork) {
return;
}
isdoingwork = true;
for (inx = lastUsedInx + 1; inx < 100; inx++) {
if (this->Stop) {
break;
}
// output into a text box
emit numberChanged(inx);
lastUsedInx = inx;
// process events, to allow stop to be processed using signals and slots
(QAbstractEventDispatcher::instance(pThread))->processEvents(QEventLoop::AllEvents);
QThread::msleep(800);
}
isdoingwork = false;
}
I am trying to create a new thread gpsthread which should run in the back ground, and store the value.
class gpsthread: public QThread{
Q_OBJECT
private:nrega_status_t status2;
public:
explicit gpsthread(QObject *parent = 0):QThread(parent) {
// QTimer *t = new QTimer(this);
// connect(t, SIGNAL(timeout()), this, SLOT(processgps()));
// t->start(10000);
}
void run(){
qDebug()<<"inside gps thread\n";
QTimer *t = new QTimer(this);
connect(t, SIGNAL(timeout()), this, SLOT(processgps()));
t->start(10000);
}
public slots:void processgps(){
int status2;
status2=gps_management();
}
};
My main class is quickview.
int main(int argc, char *argv[])
{
QString file = "qml/main.qml";
QApplication app(argc, argv);
TranslationTest myObj;
QuickView view;
subthread object;
gpsthread obj;
gprsthread gprs;
view.rootContext()->setContextProperty("rootItem", (QObject *)&myObj);
obj.start();
//from subthread
QObject::connect(&object, SIGNAL(batterytoqml(QVariant,QVariant)),item, SLOT(frombattery(QVariant,QVariant)));
QObject::connect(&gprs, SIGNAL(gprstoqml(QVariant)),item, SLOT(fromgprs(QVariant)));
return app.exec();
}
I have tried this as well
class gpsthread: public QThread{
Q_OBJECT
private:nrega_status_t status2;
public:QTimer* t;
explicit gpsthread(QObject *parent = 0):QThread(parent) {
// QTimer *t = new QTimer(this);
// connect(t, SIGNAL(timeout()), this, SLOT(processgps()));
// t->start(10000);
}
void run(){
qDebug()<<"inside gps thread\n";
t = new QTimer(this);
connect(t, SIGNAL(timeout()), this, SLOT(processgps()));
t->start(10000);
exec();
}
public slots:void processgps(){
int status2;
status2=gps_management();
}
};
But it is giving error saying
QObject: Cannot create children for a parent that is in a different thread
If I create object in constructor then also it will give the same error because the object will be created in the main thread.
How to resolve this?
Inheriting from QThread is not the recommended usage. QThread is a complete class that runs an event loop, which is generally what you need. The documentation recommends using a worker object that inherits from QObject and does work in a slot. The worker is moved into a QThread. When a connected signal is sent, the slot will run in the correct thread.
class gpsworker: public QObject
{
Q_OBJECT
public:
explicit gpsworker(QObject *parent = 0):
QObject(parent)
{}
public slots:
void processgps() {
qDebug() << "processgps()" << QThread::currentThreadId();
}
}
void OwnerClass::startWorker() {
QTimer *timer = new QTimer(this);
QThread *thread = new QThread(this);
this->worker = new gpsworker();
this->worker->moveToThread(thread);
connect(timer, SIGNAL(timeout()), this->worker, SLOT(processgps()) );
connect(thread, SIGNAL(finished()), this->worker, SLOT(deleteLater()) );
thread->start();
timer->start();
}
If you want the timer to live in the other thread as well, QTimer::start is a slot.
QObject: Cannot create children for a parent that is in a different thready
is beacuse t = new QTimer(this) in the run() is creating an object in the child thread but this which point at gpsthread is in the main thread. A simple solution is t = new QTimer() without a parent and delete the timer in the destructor. Here is an example:
class gpsthread : public QThread {
Q_OBJECT
public:
explicit gpsthread(QObject *parent = 0):
QThread(parent)
,timer(NULL) {
qDebug() << "Parent thread" << QThread::currentThreadId();
}
~gpsthread() {
quit();
wait();
delete timer;
}
protected:
void run() {
qDebug() << "Inside gps thread" << QThread::currentThreadId();
timer = new QTimer; // no parent
connect(timer, SIGNAL(timeout()), this, SLOT(processgps()));
timer->start(1000);
exec();
}
public slots:
void processgps() {
qDebug() << "processgps()" << QThread::currentThreadId();
}
private:
QTimer *timer;
};
Soon you will find out console print:
Parent thread 0x3b28
inside gps thread 0x3f10
processgps() 0x3b28
processgps() 0x3b28
processgps() 0x3b28
processgps() 0x3b28
which means processgps() is not working in your child thread. This is because this slot is a member of gpsthread which is in the main thread. A simple solution is call processgps() directly and use sleep() as timer:
class gpsthread : public QThread
{
Q_OBJECT
public:
explicit gpsthread(QObject *parent = 0):
QThread(parent)
, abort(false) {
qDebug() << "Parent thread" << QThread::currentThreadId();
}
~gpsthread() {
abort = true;
wait();
}
protected:
void run() {
while(!abort) {
sleep(1);
processgps();
}
}
public slots:
void processgps() {
qDebug() << "processgps()" << QThread::currentThreadId();
}
private:
bool abort;
};
This is not a nice solution, a recommanded way is creating a worker to do all the job and then use QObject::moveToThread() as said in QThread Document