It was my first attempt to create a thread in qt but unfortunately it isn't stopping. This is how my code look like.
// Worker class
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
forever{
if(QThread::currentThread()->isInterruptionRequested()){
printf("true");
return;
}else{
if(!QThread::currentThread()->isInterruptionRequested()){
cout<<parameter.toStdString();
}
}
}
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
// Controller class
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller(QObject *parent = nullptr):QObject(parent) {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void stop(){
workerThread.requestInterruption();
}
signals:
void operate(const QString &);
};
The forever loop isn't stopping when stop function is called.Have I done anything wrong?
Related
I'd like to insert the serial port in a separate QThread, but the application crashes. I wrote the following C++ classes
Worker.h
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
signals:
void finished();
void error(QString err);
public slots:
void process();
};
class WorkerInterface : public QObject
{
Q_OBJECT
public:
explicit WorkerInterface(QObject *parent = nullptr);
~WorkerInterface();
serialport *readSerialPort();
signals:
void threadStoppedChanged();
public slots:
void errorString(QString errorMsg);
void stopThread();
private:
QThread m_thread;
serialPort *m_serial;
};
Worker::Worker(QObject *parent)
: QObject(parent)
{
}
void Worker::process()
{
emit finished();
}
Worker.cpp
WorkerInterface::WorkerInterface(QObject *parent)
: QObject(parent)
, m_thread(this)
{
serialPort::serialPortMaster = new serialPort(nullptr);
m_serial = serialPort::serialPortMaster;
serialPort::serialPortMaster->moveToThread(&m_thread);
connect(&m_thread, SIGNAL(started()),serialPort::serialPortMaster, SLOT(Init()));
m_thread.start();
}
WorkerInterface::~WorkerInterface()
{
m_thread.quit();
m_thread.wait(1000);
if (!m_thread.isFinished())
m_thread.terminate();
}
void WorkerInterface::errorString(QString errorMsg)
{
qDebug() << "error" << errorMsg;
}
void WorkerInterface::stopThread()
{
m_thread.quit();
m_thread.wait(1000);
if (!m_thread.isFinished())
m_thread.terminate();
emit threadStoppedChanged();
}
serialPort* WorkerInterface::readSerialPort()
{
return(m_serialPort);
}
In the main.cpp I wrote the following code:
WorkerInterface workerInterface;
engine.rootContext()->setContextProperty("newserial", workerInterface.readSerialPort());
QQmlComponent component(&engine,QUrl(QStringLiteral("qrc:/Pages/Content/Qml/main.qml")));
QObject *qmlObject = component.create();
When the code arrives at the last instruction in main.cpp, the application crashes and in the QT creator console there is the following messages:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0xee18c0), parent's thread is QThread(0xc8d8b0), current thread is QThread(0x7fffffffdc60)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0xee18c0), parent's thread is QThread(0xc8d8b0), current thread is QThread(0x7fffffffdc60)
QQmlEngine: Illegal attempt to connect to serialPort(0xee1710) that is in a different thread than the QML engine QQmlApplicationEngine(0x7fffffffdc30.
Could someone help me to solve the crash?
Many thanks in advance.
Assuming that you have device which responds with text, the best and simplest way to do it is something like this:
class IODevLineReader
{
Q_OBJECT
public:
explicit IODevLineReader(QObject *parent);
public signals:
void lineWasReceived(const QString &line);
public slots:
void onReadyRead() {
QIODevice *dev = qobject_cast<QIODevice *>(sender());
while (dev && dev->canReadLine()) {
auto lineBytes = dev->readLine();
emit lineWasReceived(lineBytes);
}
}
};
Just connect QSerialPort::readyRead() to IODevLineReader::onReadyRead() and connect some slot to IODevLineReader::lineWasReceived() signal and you are done without use of threads.
And if you still insist to use thread, just use same object tree and move it to specified thread.
I have the following classes (simplified) :
class BaseJob : public QObject
{
Q_OBJECT
public:
QMutex *getMutex() const {
return &mutex;
}
virtual ~BaseJob() = 0;
private:
static QMutex mutex;
};
class Worker1 : public QObject
{
Q_OBJECT
public:
Worker1(QMutex *mutex) : m_mutex(mutex) {}
public slots:
void work() {
QMutexLocker lock(m_mutex);
qInfo() << "worker1 is running";
}
private:
QMutex *m_mutex;
};
class Worker2 : public QObject
{
Q_OBJECT
public:
Worker2(QMutex *mutex) : m_mutex(mutex) {}
public slots:
void work() {
QMutexLocker lock(m_mutex);
qInfo() << "worker2 is running";
}
private:
QMutex *m_mutex;
};
class Job1 : public BaseJob
{
Q_OBJECT
public:
Job1() : m_worker(new Worker1(this->getMutex())) {
connect(this, SIGNAL(doWork()),
m_worker, SLOT(work()));
connect(&m_thread, SIGNAL(finished()),
m_worker, SLOT(deleteLater()));
m_worker->moveToThread(&m_thread);
m_thread.start();
}
void work() {
emit doWork();
}
virtual ~Job1() {
m_thread.quit();
m_thread.wait();
}
signals:
void doWork();
private:
QThread m_thread;
Worker1 *m_worker;
};
class Job2 : public BaseJob
{
Q_OBJECT
public:
Job2() : m_worker(new Worker2(this->getMutex())) {
connect(this, SIGNAL(doWork()),
m_worker, SLOT(work()));
connect(&m_thread, SIGNAL(finished()),
m_worker, SLOT(deleteLater()));
m_worker->moveToThread(&m_thread);
m_thread.start();
}
void work() {
emit doWork();
}
virtual ~Job2() {
m_thread.quit();
m_thread.wait();
}
signals:
void doWork();
private:
QThread m_thread;
Worker2 *m_worker;
};
and then i call the jobs using :
void Widget::on_pushButton_clicked()
{
m_job1->work();
m_job2->work();
}
Workers need to have a public slot, work() that should be protected by a critical section to prevent concurrent write access to the same file.
So far, i have added a QMutex in the base class BaseJob so i can pass a reference to this mutex, through Worker1 and Worker2 ctor and finally inside work() function.
And it works but is there a better approach to achieve this ?
Thanks.
I have two subclasses of QObject:
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
signals:
public slots:
void process();
};
And
class ThreadController : public QObject
{
Q_OBJECT
public:
explicit ThreadController(QObject *parent = 0);
public slots:
void sleep();
void wakeUp();
private:
QMutex *mutex;
QWaitCondition *wc;
};
void ThreadController::sleep(){
mutex->lock();
wc->wait(mutex);
mutex->unlock();
}
void ThreadController::wakeUp(){
mutex->lock();
wc->wakeOne();
mutex->unlock();
}
******************************************************
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
QThread* thread = new QThread;
Worker* worker = new Worker;
ThreadController*controller=new ThreadController();
controller->moveToThread(thread);
worker->moveToThread(thread);
QObject::connect(thread,&QThread::started,worker,&Worker::process);
QObject::connect(&w,&MainWindow::wakeClicked,controller,&ThreadController::wakeUp);
QObject::connect(&w,&MainWindow::sleepClicked,controller,&ThreadController::sleep);
thread->start();
return a.exec();
}
I move the worker and controller to the same QThread. I want to control the thread (put to sleep or wake up) by the ThreadController and the worker just doing its work, now the idea can not work, the worker can doing process but the controller can not work, I don't know what's wrong with my code.
Thanks #thuga yes,just as you say. Worker::process is a infinite loop like that:
void Worker::process(){
int a=(int)QThread::currentThreadId();
emit sendThreadId(a);
int b=0;
while (b<1000000) {
b++;
emit processResult(b);
QThread::msleep(1);// signal emit too quickly will block the main thread,
//so i just let the loop slow down
}
}
Why ThreadController::sleep is never executed if the process is infinite loop,but the ThreadController::wakeup can be executed i had tested.
QObject::connect(&w,&MainWindow::wakeClicked,controller,&ThreadController::wakeUp,Qt::DirectConnection);
yes the directconnection let it work.thanks!And i havs some questions:when a QThread uses QWaitCondition::wait() will stop the event loop in it? So any signal sends to the thread will not be processed anymore?
i want to reuse the QThread in my program.When the worker finishs its work will send a signal to the controller,and the controller will execute the sleep()function.Next time the data comes,controller wakeup() the thread and the worker goes to work again. Has any other solution to suit this case ?Reuse QThread or Delete it and new it(which Performance loss more)?
#thuga I need the signal&slot in the workerthread.But QtConcurrent::run could not provide such a mechanism.You should be correct that we don't have to put the thread to sleep when there is nothing to do. And i just finished the job with your help. Although my solution is not good,i posted all the code as shown below:
Worker Class for doing heavy jobs.
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
signals:
void processResult(int);
void sendThreadId(int);
public slots:
void process();
};
void Worker::process(){
QEventLoop eventloop;
int a=(int)QThread::currentThreadId();
emit sendThreadId(a);
int b=0;
while (b<1000000) {
b++;
emit processResult(b);
QThread::msleep(1);
eventloop.processEvents();
}
qDebug()<<"Worker::process() called";
qDebug()<<QThread::currentThreadId();
eventloop.exec();
}
ThreadController class for controling the worker QThread to sleep or wakeup.
class ThreadController : public QObject
{
Q_OBJECT
public:
explicit ThreadController(QObject *parent = 0);
~ThreadController();
signals:
void statusChanged(QString);
public slots:
void sleep();
void wakeUp();
private:
QMutex *mutex;
QWaitCondition *wc;
};
ThreadController::ThreadController(QObject *parent) : QObject(parent)
{
mutex=new QMutex();
wc=new QWaitCondition();
}
ThreadController::~ThreadController(){
delete mutex;
delete wc;
}
void ThreadController::sleep(){
qDebug()<<"ThreadController::sleep() called";
qDebug()<<QThread::currentThreadId();
emit statusChanged("WorkerThread sleeping");
mutex->lock();
wc->wait(mutex);
mutex->unlock();
}
void ThreadController::wakeUp(){
qDebug()<<"ThreadController::wakeUp() called";
qDebug()<<QThread::currentThreadId();
emit statusChanged("WorkerThread wakening");
mutex->lock();
wc->wakeAll();
mutex->unlock();
}
MainWindow Class for making a simple GUI(two QPushButtons and several QLabels)
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void wakeClicked();
void sleepClicked();
public slots:
void setMainId(int);
void setThreadId(int);
void setThreadStatus(QString);
void setThreadCount(int);
private:
QLabel*l_mainThreadId;
QLabel*l_workerThreadId;
QLabel*l_workerThreadStatus;
QLabel*l_workerThreadCount;
QString count;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setWindowTitle("Threading Test");
QWidget *window=new QWidget;
QVBoxLayout *mainLayout=new QVBoxLayout;
QHBoxLayout *mainLayout1=new QHBoxLayout;
QHBoxLayout *mainLayout2=new QHBoxLayout;
QLabel*label1=new QLabel("Main Thread ID:");
l_mainThreadId=new QLabel;
int a=(int)QThread::currentThreadId();
QString idlabel=QString::number(a);
l_mainThreadId->setText(idlabel);
mainLayout1->addWidget(label1);
mainLayout1->addWidget(l_mainThreadId);
QPushButton* wakeButton=new QPushButton("WakeUp");
QPushButton*sleepButton=new QPushButton("Sleep");
mainLayout2->addWidget(wakeButton);
mainLayout2->addWidget(sleepButton);
mainLayout->addLayout(mainLayout1);
mainLayout->addLayout(mainLayout2);
QVBoxLayout *threadLayout=new QVBoxLayout;
QHBoxLayout *threadLayout1=new QHBoxLayout;
QHBoxLayout *threadLayout2=new QHBoxLayout;
QHBoxLayout *threadLayout3=new QHBoxLayout;
QLabel *label2=new QLabel("Worker Thread ID:");
l_workerThreadId=new QLabel;
threadLayout1->addWidget(label2);
threadLayout1->addWidget(l_workerThreadId);
QLabel *label3=new QLabel("Worker Thread Status:");
l_workerThreadStatus=new QLabel;
threadLayout2->addWidget(label3);
threadLayout2->addWidget(l_workerThreadStatus);
QLabel *label4=new QLabel("Counting Result:");
l_workerThreadCount=new QLabel;
threadLayout3->addWidget(label4);
threadLayout3->addWidget(l_workerThreadCount);
threadLayout->addLayout(threadLayout1);
threadLayout->addLayout(threadLayout2);
threadLayout->addLayout(threadLayout3);
QHBoxLayout *layout=new QHBoxLayout;
layout->addLayout(mainLayout);
layout->addLayout(threadLayout);
window->setLayout(layout);
setCentralWidget(window);
setThreadStatus("WorkerThread wakening");
QObject::connect(wakeButton,&QPushButton::clicked,this,&MainWindow::wakeClicked);
QObject::connect(sleepButton,&QPushButton::clicked,this,&MainWindow::sleepClicked);
}
MainWindow::~MainWindow()
{
}
void MainWindow::setMainId(int id){
QString idlabel=QString::number(id);
l_mainThreadId->setText(idlabel);
}
void MainWindow::setThreadId(int id){
QString idlabel=QString::number(id);
l_workerThreadId->setText(idlabel);
}
void MainWindow::setThreadStatus(QString s){
l_workerThreadStatus->setText(s);
}
void MainWindow::setThreadCount(int i){
count = QString::number (i) ;
l_workerThreadCount->setText(count);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
QThread* thread = new QThread;
Worker* worker = new Worker;
ThreadController*controller=new ThreadController();
controller->moveToThread(thread);
worker->moveToThread(thread);
QObject::connect(thread,&QThread::started,worker,&Worker::process); QObject::connect(worker,&Worker::processResult,&w,&MainWindow::setThreadCount);
QObject::connect(worker,&Worker::sendThreadId,&w,&MainWindow::setThreadId);
QObject::connect(&w,&MainWindow::wakeClicked,controller,&ThreadController::wakeUp,Qt::DirectConnection);
QObject::connect(&w,&MainWindow::sleepClicked,controller,&ThreadController::sleep);
QObject::connect(controller,&ThreadController::statusChanged,&w,&MainWindow::setThreadStatus);
thread->start();
return a.exec();
}
The Program:
I am a beginner of qt.I adopted the way recommended by QThread Class in qt documentation.
The method used in documentation is as follows.
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
So I have Class SocketThreadCtrler and Class SocketWorker respectively.
Concrete code is as follows.
socketthreadctrler.h is as follows.
#ifndef SOCKETTHREADCTRLER_H
#define SOCKETTHREADCTRLER_H
#include <QObject>
#include <QThread>
#include "socketworker.h"
class SocketThreadCtrler : public QObject
{
Q_OBJECT
private:
QThread workThread;
public:
explicit SocketThreadCtrler(QObject *parent = 0);
~SocketThreadCtrler();
private:
void connSigAngSlots();
public slots:
void login(QString acnt, QString passwd);
void EmitSigCheckLogin(bool loginStatus);
signals:
void SIG_CONECTSERVER();
void SIG_WRITEMSG(QByteArray&);
void SIG_CHECKLOGIN(bool);
private:
SocketWorker* worker;
// MainUI* mainUI;
};
#endif // SOCKETTHREADCTRLER_H
socketThreadCtrler.cpp
#include "header/backend/communication/socketthreadctrler.h"
#include "header/backend/communication/smessage1.h"
SocketThreadCtrler::SocketThreadCtrler(QObject *parent) : QObject(parent)//, worker(NULL)
{
// SocketWorker* worker = new SocketWorker();
worker=new SocketWorker;
worker->moveToThread(&workThread);
connSigAngSlots();
workThread.start();
emit SIG_CONECTSERVER();
}
SocketThreadCtrler::~SocketThreadCtrler()
{
workThread.quit();
workThread.wait();
}
void SocketThreadCtrler::connSigAngSlots()
{
connect(&workThread,SIGNAL(finished()),worker,SLOT(deleteLater()));
connect(this,SIGNAL(SIG_CONECTSERVER()),worker,SLOT(connectServer()),Qt::QueuedConnection);
connect(this,SIGNAL(SIG_WRITEMSG(QByteArray&)),worker,SLOT(writeMsg(QByteArray&)));
connect(worker,SIGNAL(Sig_LoginStatus(bool)),this,SLOT(EmitSigCheckLogin(bool)));
}
void SocketThreadCtrler::login(QString acnt, QString passwd)
{
//如何给login专门开一个线程
SMessage1 msg1(acnt, passwd);
emit SIG_WRITEMSG(msg1.getMsg());
}
void SocketThreadCtrler::EmitSigCheckLogin(bool loginStatus)
{
emit SIG_CHECKLOGIN(loginStatus);
}
socketWorker.h
#ifndef SOCKETWORKER_H
#define SOCKETWORKER_H
#include <QObject>
#include <QTcpSocket>
#include "messagefactory.h"
#include "rmessage.h"
#include "message.h"
#include "auxfuncset.h"
#include "smessage.h"
class SocketWorker : public QObject
{
Q_OBJECT
private:
static const quint16 serverPort=12345;
static const QString serverAddr;
QTcpSocket sock;
public:
explicit SocketWorker(QObject *parent = 0);
void WorkerEmitSigLogin(bool loginStatus);
private:
bool isServerMsg();
quint32 getMsgLen();
void getMsg(quint32 msgLen, QByteArray& msgBytes);
signals:
void Sig_LoginStatus(bool loginStatus);
public slots:
void connectServer();
void writeMsg(QByteArray&Msg);
void procMsg();
//private:
};
#endif // SOCKETWORKER_H
socketWorker.cpp
#include "header/backend/communication/socketworker.h"
const QString SocketWorker::serverAddr("127.0.0.1");
SocketWorker::SocketWorker(QObject *parent) : QObject(parent)
{
}
void SocketWorker::connectServer()
{
QString tmp=serverAddr;
sock.connectToHost(tmp,serverPort);
connect(&sock,SIGNAL(readyRead()) ,this, SLOT(procMsg()));
}
void SocketWorker::writeMsg(QByteArray& Msg)
{
sock.write(Msg);
// QAbstractSocket will start sending data automatically
// once control goes back to the event loop
sock.flush();
}
void SocketWorker::procMsg()
{
if(!isServerMsg()) return;
quint32 msgLen=0;
msgLen=getMsgLen();
if(0 == msgLen) return;
QByteArray msgBytes;
getMsg(msgLen,msgBytes);
RMessage* msg = MessageFactory::createMessage(msgBytes);
msg->process(this);
delete msg;
}
bool SocketWorker::isServerMsg()
{
//MSGHEADER
quint32 msgHead=0;
while(AuxFuncSet::readQuint32(&sock, msgHead))
{
if(msgHead == Message::MSGHEADER)
{
return true;
}
}
return false;
}
quint32 SocketWorker::getMsgLen()
{
while(sock.bytesAvailable()<sizeof(quint32));
quint32 msgLen=0;
AuxFuncSet::readQuint32(&sock,msgLen);
return msgLen;
}
void SocketWorker::getMsg(quint32 msgLen, QByteArray& msgBytes)
{
while(sock.bytesAvailable()<msgLen);
msgBytes = sock.read(msgLen);
}
void SocketWorker::WorkerEmitSigLogin(bool loginStatus)
{
emit Sig_LoginStatus(loginStatus);
}
The error message provided by compiler is
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0xb988c8), parent's thread is QThread(0xb93528), current thread is QThread(0x8dfe2c)
From my debugging, I find the key section is signal-slot connect part that causes this problem.
Specifically,there are four connection statements.
i.e.,
connect(&workThread,SIGNAL(finished()),worker,SLOT(deleteLater()));
connect(this,SIGNAL(SIG_CONECTSERVER()),worker,SLOT(connectServer()),Qt::QueuedConnection);
connect(this,SIGNAL(SIG_WRITEMSG(QByteArray&)),worker,SLOT(writeMsg(QByteArray&)));
connect(worker,SIGNAL(Sig_LoginStatus(bool)),this,SLOT(EmitSigCheckLogin(bool)));
But only the middle two statements causes this error.Concretely,if I delete the middle two statements,i.e.,
connect(this,SIGNAL(SIG_CONECTSERVER()),worker,SLOT(connectServer()),Qt::QueuedConnection);
connect(this,SIGNAL(SIG_WRITEMSG(QByteArray&)),worker,SLOT(writeMsg(QByteArray&)));
then there are no errors.
So I am confused.After one and a half hour,I still don't find a effective solution and a reasonable explanation.So I think I need help.Thanks in advance.
I have a class like this:
class myClass:public QThread
then in its constructor I did:
myClass::myClass(){
moveToThread(this);
...
}
It seems that all the member slots are actually working on the worker thread.
But how could I stop the thread during the deconstruction in this case?
Simply don't do threading like that.
The proper way to use moveToThread() is described in Qt docs:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
or by inheriting QThread, like:
class WorkerThread : public QThread
{
Q_OBJECT
void run() Q_DECL_OVERRIDE {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
}
but not both of them at the same time.
More on that topic here