I have a main class that launches a thread that performs a single action. I am trying to catch when the thread operations finish.
The main class:
// .h
class MainClass : public QObject
{
Q_OBJECT
public:
QThread thread;
// ...
public slots:
void onFinish();
}
// .cpp
void MainClass::startThread()
{
thread = new QThread();
worker = new Worker();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
// connect(worker, SIGNAL(finished()), this, SLOT(onFinish()), Qt::DirectConnection);
}
void MainClass::onFinish()
{
std::cout << "Finished!" << std::endl << std::flush;
}
The Worker class for the thread:
// .h
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
~Worker();
public slots:
void process();
signals:
void finished();
}
// .cpp
void Worker::process()
{
// ...Do stuff...
emit finished();
}
Executing startThread does not print "Finished!". Why?
I have noticed that if I add Qt::DirectConnection to the line that connects to onFinish() (like in the commented line), the message is printed. But what can I do if I want to execute the onFinish() actions in the thread of the main class?
Edit 1
In addition, it seems that the finished() -> quit() connect is also not working, because if I call thread->isFinished() or thread->isRunning(), after sleeping in the main thread to be sure that the thread task is completed, I obtain false and true, respectively.
Edit 2
Since it may also be relevant, here is the main.cpp:
int _tmain(int argc, _TCHAR* argv[])
{
QCoreApplication a(argc, argv);
std::unique_ptr <MainClass> mc = std::make_unique <MainClass>();
mc->startThread();
mc->thread->wait();
return a.exec();
}
On a first look I would say that the order of connections is important. The deleteLater() slots should always be the last methods.
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
Sitenode - QRunnable
You might consider to use QRunnable instead, which is designed for short running tasks/jobs. You can combine them with QObject or QFuture/QFutureWatcher to get notified as soon as they a finished.
Update 1
Your main-event loop never starts and it looks like you want to exit the application when the task finishes. I don't think this is a nice implementation but these modifcations should work for you:
int _tmain(int argc, _TCHAR* argv[])
{
QCoreApplication a(argc, argv);
std::unique_ptr<MainClass> mc = std::make_unique<MainClass>();
mc->startThread();
// do not wait!
// let the main-event-loop handle events -> a.exec()
// and quit() application by signal/slot
return a.exec();
}
void MainClass::startThread()
{
thread = new QThread();
worker = new Worker();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), qApp, SLOT(quit()));
}
Related
i have a worker thread which i try to stop through a button. i sometimes get the following error:
Fatal: QThread: Destroyed while thread is still running
Here is my code. By pushing the disconnect button a signal is emitted to stop the thread's while loop.
if (ui->connectButton->text() == "Connect") {
mUDPThread = new QThread;
mUDPWorker = new UDPThread(ui->HostTextEdit->toPlainText(), ui->portTextEdit->toPlainText().toInt());
mUDPWorker->moveToThread(mUDPThread);
connect(mUDPThread, SIGNAL(started()), mUDPWorker, SLOT(process()));
connect(mUDPWorker, SIGNAL(finished()), mUDPThread, SLOT(quit()));
connect(mUDPWorker, SIGNAL(finished()), mUDPWorker, SLOT(deleteLater()));
connect(mUDPWorker, SIGNAL(finished()), mUDPThread, SLOT(deleteLater()));
connect(this, SIGNAL(onExitThread()), mUDPWorker, SLOT(onExitThread()));
connect(this, SIGNAL(onDataIncome(QString)), mUDPWorker, SLOT(onDataIncome(QString)));
mUDPThread->start();
ui->connectButton->setText("Disconnect");
} else if (ui->connectButton->text() == "Disconnect") {
emit onExitThread();
ui->connectButton->setText("Connect");
}
the worker Thread:
void UDPThread::process() {
while (isRunning) {
QCoreApplication::processEvents();
...
}
emit finished();
}
void UDPThread::onExitThread() {
qDebug() << "onExitThread" << isRunning;
isRunning = false;
}
while (isRunning)
You are blocking the thread right there, its event loop cannot spin in order to receive signals, it can only send signals to other threads with spinning event loops.
You need to make your worker non-blocking, split the work in cycles, in between the event loop gets to spin and receive signals. In pseudocode:
if (isRunning) {
doWorkCycle();
scheduleNextWorkCycle();
} else emit finished();
There is an example you can look at here.
Solved it. The error was within the connect commands:
connect(mUDPWorker, SIGNAL(finished()), mUDPThread, SLOT(deleteLater()));
to
connect(mUDPThread, SIGNAL(finished()), mUDPThread, SLOT(deleteLater()));
result:
connect(mUDPThread, SIGNAL(started()), mUDPWorker, SLOT(process()));
connect(mUDPWorker, SIGNAL(finished()), mUDPThread, SLOT(quit()));
connect(mUDPWorker, SIGNAL(finished()), mUDPWorker, SLOT(deleteLater()));
connect(mUDPThread, SIGNAL(finished()), mUDPThread, SLOT(deleteLater()));
connect(this, SIGNAL(onExitThread()), mUDPWorker, SLOT(onExitThread()));
connect(this, SIGNAL(onDataIncome(QString)), mUDPWorker, SLOT(onDataIncome(QString)));
In the Worker Class I have two functions who works and control the thread, start() and abort()
void Worker::requestWork()
{
mutex.lock();
_working = true;
_abort = false;
qDebug()<<"Le thread travail de"<<this->myId<<" "<<thread()->currentThreadId();
mutex.unlock();
emit workRequested();
}
void Worker::abort()
{
mutex.lock();
if(_working) {
_abort = true;
qDebug()<<"Le thread "<<thread()->currentThreadId()<<" s'arrete";
}
mutex.unlock();
}
As you can see the workrequest emit a signal saying to the thread to start to work. And because the Worker Class is in the thread, how can I pause or restore it? From the worker class? from the MainWindow?
and now the Whole code.Mainwindow Class.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QObject::connect(ui->lineEdit,SIGNAL(textChanged(QString)),this,SLOT(command(QString)));
thread = new QThread();
worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(valueChanged(QString)), ui->label, SLOT(setText(QString)));
connect(worker, SIGNAL(workRequested()), thread, SLOT(start()));
connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()), Qt::DirectConnection);
}
//QLineEdit send signal to slot "command" permit to control the thread
void MainWindow::command(QString text){
qDebug()<<"le message a bien ete intercepte";
qDebug()<<ui->lineEdit->text();
if (text.contains("help"))
qDebug()<<"heeeelp";
if (text.contains("pause")){
worker->Paused();
if(thread->isRunning()){}
//cond.wait()
}
if (text.contains("restart")){
worker->Restarted();
if (!thread->isRunning()){}
//cond.wakeAll();
}
if (text.contains("stopped")){
worker->Paused();
thread->wait();
}
if (text.contains("start")){
worker->requestWork();
}
if (text.contains("destroyed")){
worker->destroyed();
}
}
So my question is: how to pause and restart the thread from the command() slot when the User insert "Pause" ?
I think you mess up the two possible ways of doing work with QThread:
One is to have an object (Worker) moved to a start()ed QThread. This way you do work by calling slots on the Worker, the work is done when the slots execute. You don't pause/resume this - when the slot is done, QThread will wait for new work, the same way QApplication waits for events when it's idle.
Second way is subclassing QThread, reimplementing run() and creating (some sort of) Worker there. Using this way you must create your "work queue" with something similar to your state variables (_working, _done, wait conditions, etc), because the thread will exit if it leaves run(), you must pause/resume yourself.
I was for hours trying to reduce this error to a minimal example, but could not succeed.
I have a GUI with a QPushButton importPCDButton. It should open a QFileDialog and import the selected file. I want to use a thread hoping that the dialog vanishes as soon as I send an update signal to my GUI. So I have an ImportPCD worker class derived from QObject implementing a public slot with the name process(). Inside this, I open the QFileDialog, etc:
void ImportPCD::process() {
std::cout << "foo \n";
QString file_abs = QFileDialog::getOpenFileName(
&*(getControl()->getGuiPtr()),
tr("OpenFile"),
"../data/",
tr("Point Cloud File(*pcd);;ASCII - File(*.asc);;All Files(*)"));
int index = file_abs.lastIndexOf("/");
int size = file_abs.size();
int position = size-index-1;
QString file = file_abs.right(position);
index = file.indexOf(".");
file = file.left(index);
getControl()->setTreeID(file.toStdString());
QString abort;
if(file!=abort)
{
this->path = file_abs.toStdString();
cloudRGB = import ();
emit updateUI();
computeNormals (cloudRGB);
emit updateUI();
principalCurvatures = computeCurvature ();
setRGB ();
getControl()->setCloudPtr(getCloudRGB());
}
emit finished();
}
It also implements the slots updateUI() and finished().
I have a public slot importPCDFile() in my GUI:
void PCLViewer::importPCDFile() {
boost::shared_ptr<QThread> thread_ptr (new QThread);
boost::shared_ptr<ImportPCD> importWorker (new ImportPCD(control));
importWorker->moveToThread(&*thread_ptr);
// connect (ui->importPCDButton , SIGNAL(clicked()), &*thread_ptr, SLOT(start()));
connect (&*thread_ptr, SIGNAL(started()), &*importWorker, SLOT(process()));
// connect (ui->importPCDButton , SIGNAL(clicked()), &*importWorker, SLOT(process()));
connect (&*importWorker, SIGNAL(updateUI()), this, SLOT(updateUI()));
connect (&*importWorker, SIGNAL(finished()), this, SLOT(updateUI()));
connect (&*importWorker, SIGNAL(finished()),&*thread_ptr, SLOT(quit()));
// connect (&*importWorker, SIGNAL(finished()),&*thread_ptr, SLOT(quit()));
connect (&*thread_ptr, SIGNAL(finished()), &*thread_ptr, SLOT(deleteLater()));
// connect (&*thread_ptr, SIGNAL(finished()), &*importWorker, SLOT(deleteLater()));
thread_ptr->start();
}
This method is invoked when I press the button and I get the error:
QThread: Destroyed while thread is still running
I get every time the message: QObject::moveToThread: Cannot move objects with a parent
mainwindow.cpp:
QTimer *timer_ = new QTimer(this);
Device* device = new Device(this);
QThread* thread = new QThread(this);
device->moveToThread(thread);
connect(timer_, SIGNAL(timeout()), device, SLOT(checkConnection()));
connect(device, SIGNAL(checkCompleted()), this, SLOT(doSomethingWhenItIsDone()));
timer_->start(3000);
Device.cpp:
Device::Device(QObject *parent) :
QObject(parent)
{
}
void Device::checkConnection() {
qDebug() << "checkConnection:" << QThread::currentThreadId();
//do something
emit checkCompleted();
}
this inside Device constructor means that Device has a parent and this parent in your case lives in main GUI thread, so Qt tells you that you can't move to another thread object which has parent. So try to use next:
QTimer *timer_ = new QTimer(this);
Device* device = new Device;//no parent
QThread* thread = new QThread(this);
Also you should start your thread with:
thread->start();
Also you need delete your object because it has no parent and it is your responsibility now. The most common way is to use some signal to indicate that worker already done all needed work. For example:
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
I'm trying to correct a large program for memory leaks and threads that are not stopped. I know I have some, but I'm not sure about how to properly identify and kill them, so I started playing with some canonical examples, and I'm already having those.
First I tried the simplest thing:
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
return a.exec();
}
That gives me one (1) running thread in the Task Manager.
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
WorkerOne *w = new WorkerOne();
QTimer::singleShot(3456, w, SLOT(stop()));
return a.exec();
}
This one gives me 1 before starting the worker, then 2 until the thread actually starts (process is called), then 3 until the singleShot signal is captured and the worker deleted and then 2 again. So I'm having something loose there.
And this is the code for WorkerOne:
WorkerOne::WorkerOne(QObject *parent)
: QObject(parent)
, m_stop(false) {
QThread* thread = new QThread;
this->moveToThread(thread);
connect(this, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), this, SLOT(process()));
connect(this, SIGNAL(finished()), thread, SLOT(quit()));
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
WorkerOne::~WorkerOne() {}
void WorkerOne::process() {
while(!m_stop) {
QEventLoop loop; QTimer::singleShot(1000, &loop, SLOT(quit())); loop.exec();
}
emit finished();
}
void WorkerOne::stop() {
m_stop = true;
}
void WorkerOne::errorString(QString err) { }
The platform is Qt 5.2.1 with mingw48_32 compiler.
I think I am following the steps in threading howto from Maya Posch's blog, but maybe I am missing something.
Your implementation of the worker object is literally upside down. It's QThread's job to spin the event loop. Your worker object should simply be driven by slot calls and incoming events. A processing busy loop idiom uses a zero-length timer to stay active while allowing the event loop to receive events and quit, with no need for extra flags.
Here's how to do it:
class WorkerOne : public QObject {
Q_OBJECT
QBasicTimer m_timer;
void processChunk() {
...
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() == m_timer.timerId()) processChunk();
}
public:
WorkerOne(QObject * parent = 0) : QObject(parent) {}
Q_SLOT void start() { m_timer.start(0, this); }
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
WorkerOne worker;
QThread thread;
thread.start();
worker.start();
worker.moveToThread(&thread);
a.connect(&thread, SIGNAL(finished()), SLOT(quit()));
QTimer::singleShot(3456, &thread, SLOT(quit()));
return a.exec();
}
When the timer times out, the thread's event loop quits, the thread finishes, the application's event loop is quit, and, finally, the thread and the worker get destroyed.
A zero-length timer is not really a timer, just an idiom that means: invoke me as soon as the event loop is entered and there's nothing else to do. Doing the below would be a premature pessimization as there's be a memory allocation per each round through the event loop - not using the timer would be worse!
class WorkerOne : public QObject {
Q_OBJECT
Q_INVOKABLE void processChunk() {
...
// A lame attempt to call ourselves again from the event loop.
// It works, but has lot more overhead than a zero-length timer!
QMetaObject::invokeMethod(this, "processChunk", Qt::QueuedConnection);
}
public:
WorkerOne(QObject * parent = 0) : QObject(parent) {}
Q_SLOT void start() {
QMetaObject::invokeMethod(this, "processChunk", Qt::QueuedConnection);
}
};