I've a main thread for the GUI, in which is run the MainWindow object,
in its constructor I create a new worker object and a QThread object and the i move the worker to the thread, the problem is that when printing their ids they are the same:
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
std::cout<<"MAIN_ID "<< QThread::currentThreadId()<<std::endl;
QThread *t_pc = new QThread;
worker *pc_w;
pc_w = new pc_worker();
pc_w->moveToThread(t_pc);
t_pc->start();
pc_w->initialize();
// ...
}
worker.cpp
worker::worker(QObject *parent) : QObject(parent) {
}
void worker::initialize() {
std::cout << "INITIALIZE " << QThread::currentThreadId() << std::endl;
}
I get:
MAIN_ID 0x7f4009ccb780
INITIALIZE 0x7f4009ccb780
What's wrong?
Answer: moveToThread does work, just not in the way you expected.
Looks like after calling pc_w->moveToThread(t_pc) you expected all member functions of pc_w to be called in t_pc now. However, that is not what moveToThread() does.
The purpose of moveToThread() is to change QObject "thread affinity" or in other words thread where an object lives. But on the basic level everything it gives you is just the guarantee that all the object`s slots connected to any signal via Qt::QueuedConnection will be invoked (run) in that particular thread.
Member functions still run in the thread you invoke them from. In your case, you call initialize() from the GUI thread, so QThread::currentThreadId() gives you the id of that thread.
I really recommend to read official doc on thread affinity and this article on thread event loops.
QThread* thread = new QThread;
Worker* worker = new Worker;
// Uses Qt::AutoConnection (default)
// which will be transalted into Qt::QueuedConnection
QObject::connect(thread, &QThread::started, worker, &Worker::initialize);
std::cout<<"MAIN_ID "<< QThread::currentThreadId()<<std::endl;
worker->moveToThread(thread);
thread->start();
Output:
MAIN_ID 0000000000003E5C
INITIALIZE 0000000000003DAC
The solution trivelt proposed artificially puts "call initialize()" event into the thread event loop reaching the same effect. However, it does not perform any compile-time checks ("initialize" is specified as a string).
Try to use invokeMethod instead of diretly calling pc_w->initialize(), e.g.:
QMetaObject::invokeMethod(pc_w, "initialize", Qt::QueuedConnection);
Your initialize() method should be SLOT or SIGNAL in this case.
Related
I have a simple QObject:
class Engine : public QObject
{
Q_OBJECT
public:
explicit Engine(QObject* parent = 0);
signals:
void finished();
public slots:
void start();
};
An instance Engine* engine is stored inside the main window class. When a button is pressed, the following happens:
QThread* thread = new QThread;
engine->moveToThread(thread);
connect(engine, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), engine, SLOT(start()));
connect(engine, SIGNAL(finished()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
My question is, what happens to engine after thread finishes? Can I create another thread and move engine to that thread, and repeat everything again?
What happens to engine after thread finishes?
What happens with the object is independent of it being moved to a thread. When you "move" you are not doing a real move, you are just telling to execute some of the code on a thread. The object will get destroyed as usual (out of scope or delete for heap allocated).
Can I create another thread and move engine to that thread, and repeat
everything again?
Yes, as long as the object still exists.
This is part of the main.
This code create a worker and a workerthread.
The worker is moved to the workerthread.
The worker is then waiting for the signal to ask it to work.
The worker emit a signal with result when job done.
The main supposed to catch that signal and initialize a variable in the main.
main()
{.........
// This is the variable to be changed
variableToGetFromWorker = 0;
qDebug() << "Main thread: " << QThread::currentThreadId();
QThread workerThread;
worker* stupidTom = new stupidTom(number);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, &workerThread, &QObject::deleteLater);
connect(&workerThread, SIGNAL(start()), stupidTom, SLOT(doJob()));
connect(stupidTom, SIGNAL(jobDone(int)), this, SLOT(jobDone(int)));
workerThread.start();
workerThread.wait();
...........}
// This is a slot at main. Suppose to catch the signal from the worker
void main::jobDone(int result)
{
qDebug() << "Changing variable";
variableToGetFromWorker = result;
}
This is the doJob slot of the worker.
void worker::doJob()
{
qDebug() << "worker::doJob invoked.";
qDebug() << "worker Thread:" << QThread::currentThreadId();
// Doing Job here
emit jobDone(result);
}
It is the qDebug result
Main thread: 0x7ffff7fc6780
worker::doJob invoked.
worker Thread: 0x7fffdab44700
In Debug mode, I find that the program is stop at workerThread.wait()
and never go to main::jobDone(int result). What is the reason?
Little editing on the main code:
QThread workerThread;
worker* stupidTom = new stupidTom(number);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, &workerThread, &QObject::deleteLater);
connect(&workerThread, SIGNAL(started()), stupidTom, SLOT(doJob()));
connect(stupidTom, SIGNAL(jobDone(int)), this, SLOT(jobDone(int)));
connect(stupidTom, SIGNAL(jobDone(int)), &workerThread, SLOT(quit()));
workerThread.start();
workerThread.wait();
As long as signals supposed to invoke slots. That won't be working on wait() on the main thread that supposed to run the slot or specifically jobDone signal.
Make sure to understand the difference between QThread::exec() and QThread::wait() in your application. Normally in the real app your thread would be looping (running) while the thread that launched it is still looping as well. The loop is implemented in protected QThread::exec() method. We usually don't need to call exec() explicitly but we need to allow the thread to run. You can do that by making main function to use QEventLoop for looping:
int main()
{
//...
workerThread.start();
QEventLoop eventLoop;
// here you will probably want to hook-up QEventLoop::quit() slot
// to eventually quit the process
int returnCode = eventLoop.exec();
//...
return returnCode;
}
And this is also wrong:
connect(&workerThread, SIGNAL(start()), stupidTom, SLOT(doJob()));
Instead you should create your own job object and do QObject::moveToThread for it. Here is a nice article about it. So it should rather look like:
connect(&workerThread, SIGNAL(started()), stupidTom, SLOT(doJob()));
I likely understand why you attempted to run the thread that way as above. It is similar to many C++ examples. You can also do that in Qt but you also need to realize how exactly you'll be waiting on completion of workerThread. And Qt most popular way to do the interaction between threads is with signals and slots. That is why we should use QEventLoop in main. But of course there is an alternative. For lower level plain C++ you can use mutex and condition variable to accomplish the same. Or that same wait() but then there is no signals involved.
Also watch the debug output, whether or not all the connect statements really connect proper signals to slots. In case if there is no connect it prints the warning.
In the example below (inside Qt GUI application) a new thread is started (with an event loop in which I want some work to be done):
void doWork()
{
QThread* workerThread = new QThread();
Worker* worker = new Worker();
worker->moveToThread(workerThread);
connect(workerThread, SIGNAL(started()), worker, SLOT(startWork()));
connect(worker, SIGNAL(finished()), workerThread, SLOT(quit()));
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();
}
startWork() can be a long running operation during which the application can be closed.
I expected that the application will not be closed as long as startWork() is being executed on the workerThread. It seems though, that when I close the last application window, the workerThread is gone instantaneously (during long running operation) and application closes without problems.
The questions arose:
Why was the workerThread wiped right away?
Is it some parent/child issue?
How Qt handles such situation?
Is it programmer mistake, not to call wait() on QThread (eventually)?
Even if so, I tried to wait() inside a slot for aboutToQuit() and application wasn't closed after long running operation was done (with setup as above). Only quit(); wait(); (inside the slot mentioned) allowed the application to close. Why?
QThread has, basically, a long-standing API bug: it isn't always in a destructible state. In C++, an object is considered to be in destructible state when it's safe to invoke its destructor. Destructing a running QThread is an error. A QThread is merely a thread controller, it's not the "thread" itself. Think of how QFile acts: you can destruct it at any time, whether it's open or not. It truly encapsulates the notion of a file as a resource. A QThread is too thin of a wrapper around the native (system) thread: when you destruct it, it does not terminate nor dispose of the native thread if there is one. This is a resource leak (threads are OS resources), and people trip over this issue over and over again.
When the application's main() function returns, your implementation of the C/C++ runtime library happens to terminate all of the application's threads, effectively terminating the entirety of the application. Whether this is the behavior you desire is up to you. You're supposed to quit() and wait() your event-loop-running thread. For threads without an event loop, quit() is a no-op and you must implement your own quit flag. You must wait() on the thread before you destruct it. This is to prevent race conditions.
Below is a safe wrapper for QThread. It is a final class, since you can't reimplement run. This is important, since a reimplementation of run could be done in such a way that makes quit a no-op, breaking the contract of the class.
#include <QThread>
#include <QPointer>
class Thread : public QThread {
using QThread::run; // final
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() { quit(); wait(); }
};
class ThreadQuitter {
public:
typedef QList<QPointer<Thread>> List;
private:
List m_threads;
Q_DISABLE_COPY(ThreadQuitter)
public:
ThreadQuitter() {}
ThreadQuitter(const List & threads) : m_threads(threads) {}
ThreadQuitter(List && threads) : m_threads(std::move(threads)) {}
ThreadQuitter & operator<<(Thread* thread) {
m_threads << thread; return *this;
}
ThreadQuitter & operator<<(Thread& thread) {
m_threads << &thread; return *this;
}
~ThreadQuitter() {
foreach(Thread* thread, m_threads) thread->quit();
}
};
It could be used as follows:
#include <QCoreApplication>
int main(int argc, char ** argv) {
QCoreApplication app(argc, argv);
QObject worker1, worker2;
Thread thread1, thread2;
// Style 1
ThreadQuitter quitter;
quitter << thread1 << thread2;
// Style 2
ThreadQuitter quitterB(ThreadQuitter::List() << &thread1 << &thread2);
//
worker1.moveToThread(&thread1);
worker2.moveToThread(&thread2);
thread1.start();
thread2.start();
QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
return app.exec();
}
Upon return from main, the thread quitter will quit() all worker threads. This allows the threads to wind down in parallel. Then, thread2.~Thread will wait for that thread to finish, then thread1.~Thread will do the same. The threads are now gone, the objects are threadless and can be safely destructed: worker2.~QObject is invoked first, followed by worker1.~QObject.
1) Is it parent / child issue?
Not in the case of your code - You're not parenting the QThread. Qt doesn't like you to just terminate the main thread if you've other threads running. You're likely to see it complain in the standard output that the other thread was still running when the application was terminated. However, Qt will kill the other thread, which is why there's a function to call and wait for the thread to terminate properly.
2) Is it programmer mistake not to call wait()?
Yes. If you're having issues with the Thread not quitting properly, it's because you're not handling it correctly, in which case you could open another question and show the code as to how you're handling the wait before quitting.
when I close the last application window, the workerThread is gone instantaneously
Note that there's a function in QApplication called setQuitOnLastWindowClosed, which you can set to false, to prevent the app automatically quitting on closing the last window.
I have done an application with some threads. Everything seems to work ok if I call my stopConsumer inside a keypressedEvent. But If I call it inside a destructor of closeEvent.. it fails.
My QThread class that has a run method like this one:
void Consumer::run()
{
forever {
// do something something
// do something something
// do something something
//-------------------------------- check for abort
abortMutex.lock();
if(abort) {
abortMutex.unlock();
qDebug() << "abort..";
break;
} abortMutex.unlock();
//-------------------------------- check for abort
}
qDebug() << "Consumer > emit finished()";
emit finished();
}
void Consumer::stopConsume() {
abortMutex.lock();
abort = true;
abortMutex.unlock();
}
and a method in the MainWindow:
void initConsumers()
{
consumer1 = new Consumer(....);
connect(consumer1, SIGNAL(finished()),
this, SLOT(deleteConsumer()));
consumer1->start();
}
void stopConsumer() {
if(consumer1!=NULL) {
qDebug() << "stopConsumer";
consumer1->stopConsume();
}
}
If I have a keypressed that calls stopConsumer.. it's ok, deleteConsumer is reached.
If I call stopConsumer inside the MainWindow destructor or inside a MainWindow closeEvent.. the slot deleteConsumer is never reached!
Any ideas?
Given that the Consumer class and your MainWindow have different thread affinities, the call you make to connect inside initConsumers() is likely using a Qt::QueuedConnection, which means that the deleteConsumer() slot won't get called immediately.
If you would like to ensure that the consumer gets deleted from the destructor of your main window (or equivalently, from a close event), one possible solution is to call stopConsume() on the consumer, then wait until the thread is no longer running (see http://qt-project.org/doc/qt-5.1/qtcore/qthread.html#isRunning), then call deleteConsumer() directly.
Update
Here's an example of what I described above:
consumer1->stopConsume();
consumer1->wait();
deleteConsumer();
It's not advisable to switch the connection type to Qt:DirectConnection since that will cause the deleteConsumer() function to be called from the body of Consumer::run(), which will likely crash your application.
Part of the problem here is that you're deriving from QThread, which is not how it is supposed to be used. You can read about why deriving from QThread is wrong here.
Instead, what you should be doing is deriving your class from QObject, creating a QThread object and moving the derived QObject instance to that thread.
class Consumer : public QObject
{
...
signals:
void finished();
private slots:
void run();
}
QThread pThread = new QThread;
Consumer pObject = new Consumer;
// move the pObject to the thread
pObject->moveToThread(pThread);
You can then control the thread with signals and slots.
// assuming you've added a run slot function to the Consumer class
connect(pThread, SIGNAL(started()), pObject, SLOT(run()));
connect(pObject, SIGNAL(finished()), pThread, SLOT(quit()));
connect(pObject, SIGNAL(finished()), pObject, SLOT(deleteLater()));
// Note the thread cleans itself up here, but if the app is quitting,
// waiting on the thread to finish may be required instead
connect(pThread, SIGNAL(finished()), pThread, SLOT(deleteLater()));
And start the thread: -
pThread->start();
Used this way, it also enables multiple objects to be moved to a single new thread, rather than creating a new thread per object instance.
I'm trying to create a program using threads:
the main start with a loop.
When a test returns true, I create an object and I want that object to work in an other thread
then return and start the test .
QCoreApplication a(argc, argv);
while(true){
Cmd cmd;
cmd =db->select(cmd);
if(cmd.isNull()){
sleep(2);
continue ;
}
QThread *thread = new QThread( );
process *class= new process ();
class->moveToThread(thread);
thread->start();
qDebug() << " msg"; // this doesn't run until class finish it's work
}
return a.exec();
the problem is when i start the new thread the main thread stops and wait for the new thread's finish .
The canonical Qt way would look like this:
QThread* thread = new QThread( );
Task* task = new Task();
// move the task object to the thread BEFORE connecting any signal/slots
task->moveToThread(thread);
connect(thread, SIGNAL(started()), task, SLOT(doWork()));
connect(task, SIGNAL(workFinished()), thread, SLOT(quit()));
// automatically delete thread and task object when work is done:
connect(task, SIGNAL(workFinished()), task, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
in case you arent familiar with signals/slots, the Task class would look something like this:
class Task : public QObject
{
Q_OBJECT
public:
Task();
~Task();
public slots:
// doWork must emit workFinished when it is done.
void doWork();
signals:
void workFinished();
};
I don't know how you structured your process class, but this is not really the way that moveToThread works. The moveToThread function tells QT that any slots need to be executed in the new thread rather than in the thread they were signaled from. (edit: Actually, I now remember it defaults to the tread the object was created in)
Also, if you do the work in your process class from the constructor it will not run in the new thread either.
The simplest way to have your process class execute in a new thread is to derive it from QThread and override the run method. Then you never need to call move to thread at all.