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)));
Related
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()));
}
void MainWindow::on_pushButton_clicked()
{
QFuture<int> future = QtConcurrent::run(identify); //Thread1
if (future.isFinished())
{
//DoSomething();
}
}
I have this code. I want to run the DoSomething() function after the identify function finished running. Is it possible?
You can pass the QFuture object to a QFutureWatcher and connect its finished() signal to the function or slot DoSomething().
For example:
void MainWindow::on_pushButton_clicked()
{
QFuture<int> future = QtConcurrent::run(identify); //Thread1
QFutureWatcher<int> *watcher = new QFutureWatcher<int>(this);
connect(watcher, SIGNAL(finished()), this, SLOT(doSomething()));
// delete the watcher when finished too
connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater()));
watcher->setFuture(future);
}
void MainWindow::DoSomething() // slot or ordinary function
{
// ...
}
Or you could use a nested event loop to keep the GUI responsive and have everything inside the same function:
void MainWindow::on_pushButton_clicked()
{
QFuture<int> future = QtConcurrent::run(identify); //Thread1
QFutureWatcher<int> watcher;
QEventLoop loop;
// QueuedConnection is necessary in case the signal finished is emitted before the loop starts (if the task is already finished when setFuture is called)
connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()), Qt::QueuedConnection);
watcher.setFuture(future);
loop.exec();
DoSomething();
}
I like this call layout.
auto watcher = new QFutureWatcher<int>(this);
connect(watcher, &QFutureWatcher<int>::finished,
[watcher, this] ()
{
watcher->deleteLater();
auto res = watcher->result();
// ...
});
watcher->setFuture(QtConcurrent::run(identify, param1, param2));
What is the corrent way to stop threads in Qt?
Suppose that I have a worker (LicenseChecker class) and I want to do some actions every n seconds in the process member function. I need to do it indefinitely, until someone abort my loop.
_worker = new LicenseChecker;
_thread = new QThread;
_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(newLicensesActivated(QVector<LicenseInfo>)),
this, SLOT(newLicensesActivated(QVector<LicenseInfo>)));
_thread->start();
What can I do to abort it?
The first idea that I came up with was to define the sleep function as the following:
bool LicenseChecker::sleep(int seconds)
{
QTime end_time = QTime::currentTime().addSecs(seconds);
while (QTime::currentTime() < end_time)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
QMutexLocker lock(&_abort_sync);
if (_abort)
{
return false;
}
}
return true;
}
and to check the return code of this function in my infinite loop:
while (true)
{
if (!sleep(5))
{
emit finished();
return;
}
// ...
}
And then on MainWindow's close event I need to do the following:
_worker->stop();
_thread->wait();
where stop member function just sets _abort data member to true, but the application hangs on these calls.
What is the right way to accomplish such task?
Seems that you call stop() from another thread (the main thread) but the worker instance is located in the worker thread. You can overcome this problem by carefully writing something like this to invoke a method from another thread:
void Worker::stop()
{
// make thread safe
if(QThread::currentThread() != this->thread())
{
this->metaObject()->invokeMethod(this, "stop", Qt::QueuedConnection);
return;
}
REAL CODE HERE ...
}
Besides that, i would rather use a timer to fire every n seconds to do the licence check. Something like that (example of an database connection checker):
dbCheckerThread = new QThread(this);
dbCheckerTimer = new QTimer();
dbCheckerTimer->setInterval(CHECKDBCONNECTIONINTERVALL);
dbCheckerTimer->moveToThread(dbCheckerThread);
dbChecker->moveToThread(dbCheckerThread);
connect(dbCheckerTimer, &QTimer::timeout, dbChecker, &DbConnectionChecker::checkConnection);
connect(dbCheckerThread, &QThread::started, dbCheckerTimer, static_cast<void (QTimer::*)(void)>(&QTimer::start));
dbCheckerThread->start();
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 want to make a condition in code to wait until all threads are finished in the main thread final slot, following is the test code ..
testClass::testClass()
{
m_count = 0;
m_flag = false;
for( int i = 0; i < 3; i++)
{
QThread *thread = new QThread();
WorkerThread *worker = new WorkerThread();
connect(thread, SIGNAL(started()), worker, SLOT(startThread()));
connect(worker, SIGNAL(workerFinished()), this, SLOT(threadFinished()));
connect(worker, SIGNAL(workerFinished()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
m_workerList.append(worker);
}
}
void testClass::threadFinished()
{
// wait untill all thread are finished, how to achive this ?
if(!m_flag)
{
// print << m_flag;
m_flag = true;
}
}
void WorkerThread::startThread()
{
emit workerFinished();
}
here testClass is in main thread and I want the application to wait in threadFinished slot until all threads I started in testClass constructor to finish, could anyone suggest best way to do this ?
I am using Qt 5.4.0 in Windows 7.
If using boost in addition to Qt is an option, you can use a thread group and call join_all, which waits until all threads in the group are done.
Also, QThreadPool provides the waitForDone() function ("Waits for each thread to exit and removes all threads from the thread pool."), however you might have to restructure your worker objects slightly to comply to the QRunnable interface.
A more basic solution would be to set a QVector or QMap member variable with one entry for each thread and set them to true when the respective thread finishes. Continue program execution in your slot only when all threads have reached "finished = true" state in your member variable.