Wake up QThread::exec() from another thread - c++

Consider MyThread that derives from QThread and implements it's run() routine like this
void MyThread::run() {
QThread::exec();
}
or equivalently
void MyThread::run() {
QEventLoop eventLoop(this);
eventLoop.exec();
}
In this state how does one cause the 'exec' to return by some action in another thread?
To consider the opposite case: without polling, how might one post an event from the thread back to the main thread?
Apparently signals that are emitted from a thread are handled in the same thread.

You seem to have some misunderstanding how QThreads are supposed to work. Unfortunately, you are not alone with this.
The first thing to do is to read about the correct usage in here:
How To Really, Truly Use QThreads; The Full Explanation
So, what you should write is something like this:
QThread* thread1 = new QThread;
QThread* thread2 = new QThread;
Task1* task1 = new Task1();
task1->moveToThread(thread1);
Task2* task2 = new Task2();
task2->moveToThread(thread2);
connect(task2, SIGNAL(finished()), thread1, SLOT(quit()));
connect(thread2, SIGNAL(started()), task2, SLOT(process()));
thread1->start();
thread2->start();
As you can see, I am using the quit() slot just like the aforementioned example. You will need to use then emit finished() in your task2 which tries to terminate thread1 or vice versa.

QEventLoop has a slot quit that you can call. If you put a reference to the eventloop in your mythread, you can then use that to call the slot.
Signals/slots can be used in a cross thread fashion. More information here: Signals and Slots Across Threads
QMetaObject::invokeMethod( eventloop, "quit", Qt::QueuedConnection)

Related

Properly terminate a QThread

I've have a worker class that do image acquisition in the background.
void acq::run ()
{
while (m_started)
{
blocking_call();
}
emit workFinished();
}
void acq::start ()
{
m_started = true;
run();
}
void acq::stop ()
{
m_started = false;
}
start (); stop () are slots and workFinished is a signal.
So in my UI Class, I launch the worker and I connect the signals to the slots :
m_thread = new QThread;
m_worker = new acq();
m_worker->moveToThread(m_thread);
// When the thread starts, then start the acquisition.
connect(m_thread, SIGNAL (started ()), m_worker, SLOT (start ()));
// When the worker has finished, then close the thread
connect(m_worker, SIGNAL(workFinished()), m_thread, SLOT(quit()));
m_thread->start();
At this point, I implemented the slot, closeEvent
void UIClass::closeEvent (QCloseEvent *event)
{
m_worker->stop(); // tell the worker to close
m_thread->wait(); // wait until the m_thread.quit() function is called
event->accept(); // quit the window
}
Unfortanely, m_thread->wait() is blocking. Even if the signal quit() is emmited
Thanks
edit:
I added these two connections :
connect(m_worker, SIGNAL(workFinished()), m_worker, SLOT(deleteLater()));
connect(m_thread, SIGNAL(finished()), m_thread, SLOT(deleteLater()));
and a Qdebug into acq::~acq()
The message is printed that prove, that stop is called, workFinished is emitted, deleteLater() is emitted.
A normal signal/slot connection between objects on different threads requires that the thread of the receiver object runs an event loop.
Your receiver thread does theoretically run its event loop, but the event loop is busy executing the start() slot because run() never returns.
You either need to unblock the receiver event loop or call the stop slot with a Qt::DirectConnection.
When doing the latter you need to be aware that the slot is now called in the context of the sender thread and you need to protect m_started against concurrent access.
Alternatively to using your own flag you could use QThread::requestInterruption() and QThread::isInterruptionRequested()
Add
QCoreApplication::processEvents();
to your loop and it'll work.
The reason you deadlock is that the call to acq::run() blocks and does not leave time for acq::stop() to be executed on the worker thread.
With the help of Ralph Tandetzky and Kevin Krammer I finally found a solution.
Instead of closing the thread with m_worker->stop();, I use QMetaObject::invokeMethod(m_worker, "stop", Qt::ConnectionType::QueuedConnection); and QCoreApplication::processEvents(); in the worker event loop. The behavior does not change, but I hope it will prevent race condition or other problems.
Instead of using : connect(m_worker, SIGNAL(workFinished()), m_thread, SLOT(quit()));, I use a custom slot :
connect(m_worker, &Acq::workFinished, [=]
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
QMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection);
});
We use DirectConnection because we are outside the infinite loop, so the event are not processed.
With that, I had a last problem. m_thread->wait is blocking, and I've to read events, otherwise, my custom slot will never be called. So added an event loop to my UI Class QEventLoop m_loop.
Just before m_thread->wait(), I wrote m_loop.exec();
And finally, In my custom slot, I put m_loop.quit()
connect(m_worker, &Acq::workFinished, [=]
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
QMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection);
m_loop.quit();
});
m_loop.exec() process event until quit m_loop.quit() is called. With that method, I don't even need m_thread->wait() because m_loop.quit() is called when workFinished is emitted. I don't need QMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection); anymore
Now it works like a charm
EDIT: This solution is very heavy and ugly, Qt (https://www.qtdeveloperdays.com/sites/default/files/David%20Johnson%20qthreads.pdf) sugest to use subclass and requestInteruption in my case.

main thread cannot get signal form worker thread

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.

Deconstruct object gives QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread

My code is too long to post, here is the related part:
videoClass::videoClass()
{
...
QThread* workerThread = new QThread(this);
camwrk = new cameraWorker(workerThread);
camwrk->moveToThread(workerThread);
// There are many cross thread signal slot connections happening between this and the camwrk
}
videoClass::~videoClass()
{
...
delete camwrk;
...
}
cameraWorker::cameraWorker(QThread* workerThread)
{
_belongingThread = workerThread;
...
}
cameraWorker::cameraWorker(QThread* workerThread)
{
_belongingThread = workerThread;
...
}
cameraWorker::~cameraWorker()
{
_belongingThread->quit();
_belongingThread->wait();
}
Everytime when the _belongingThread->wait(); is finished, I got the message:
QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread
What is happening here? I thought this is the correct way to use a QThread and finish it?
The QThread object itself belongs to the main thread:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run().
Apparently, QThread::wait() is implemented via events. Since cameraWorker itself is running on workerThread and not on videoClass's thread, you can't use it.
That being said, your current logic seems a little bit too complicated. You want to stop the thread when the cameraWorker gets destroyed, and you want to destroy the camera worker when its parent gets destroyed too:
QThread* workerThread = new QThread(this);
connect(camwrk, SIGNAL(destroyed()), workerThread, SLOT(quit()));
connect(this, SIGNAL(destroyed()), camwrk, SLOT(deleteLater()));
If you want to delete the workerThread after it's finished its execution simply connect finished() and deleteLater():
connect(workerThread, SIGNAL(finished()),
workerThread, SLOT(deleteLater()));
However, keep in mind that ~videoClass() will call the destructor of workerThread. Make sure that the thread doesn't run anymore before the object gets destroyed, or simply remove this from new QThread(this) to prevent ownership.

qt thread with movetothread

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.

How to tell QThread to wait until work is done ,and then finish?

I have a simple application that uses one worker thread.
This worker thread is started and initializes DownloadManager, which is responsible for downloading files from the net.
In my main application class I have the finished() SIGNAL on the thread that is emitted before the DownloadManager finishes.
My question is how to make the worker thread wait until the DownloadManager finishes its work.
Here is example code :
class Main
{
m_DownloadWorker = new DownloadWorker(this);
QObject::connect(pm_hotosDownloadWorker, SIGNAL(finished()), this, SLOT(DownloadWorkerFinished()));
m_DownloadWorker->Execute();
// do i need to do here something so the thread will wait ?
.....
void Main::DownloadWorkerFinished()
{
Log("DownloadWorkerFinished");
}
};
class DownloadWorker : public QThread
{
void DownloadWorker::Execute()
{
// do i need to do here somthing so the thread will wait ?
start();
}
void DownloadWorker::run()
{
// do i need to do here somthing so the thread will wait ?
DownloadManager* pDownloadManager = new DownloadManager(this);
pDownloadManager->download();
}
};
class DownloadManager: public QObject
{
// downloading stuff using Qt networkmanager
.....
.....
}
In cases when you have a signal that is emitted when an asynchronous operation is completed, you can always use QEventLoop to "turn" the asynchronous operation into synchronous with the current thread. It is still asynchronous, but the thread will "wait" for it to finish.
QNetworkAccessManager nam;
QEventLoop loop;
QNetworkReply *reply = nam.get(request);
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
So basically you put this in you DownloadManager class where you want to do a network request synchronously. exec() will return once the loop's quit slot has been called.
You can use QThread::exec() call to run your thread in the event loop. The thread will run it until you tell your thread to exit by calling QThread::exit(). So some sample code can look like this:
void DownloadWorker::run()
{
DownloadManager* pDownloadManager = new DownloadManager(this);
connect(pDownloadManager, SIGNAL(finished()), SLOT(exit()));
connect(pDownloadManager, SIGNAL(error()), SLOT(exit()));
pDownloadManager->download();
exec();
}
That would guarantee you that your thread won't quit until the "finished()" signal of your DownloadManager is issued.
Note: Here I put an example of how to solve your problem but I don't know your whole app code. That means there is not guarantee this code is thread safe and consistent. You need to take care of the mutexes and all the correct synchronization yourself. Be very careful ! Working with such a "low level" thread API requites good understanding of multithereading.
Hope that helps