Reputation: 5039
If I have a Qt application (that uses QCoreApplication
), and this application starts a few permanent threads, what is the proper way to shut down the application?
Is it alright to just run QCoreApplication::quit()
in one of the threads? Will this cause the other threads to be gracefully terminated (and have all their contained objects' destructors called, as opposed to being forcefully killed)?
Further details to explain the nature of the threads: they are predefined and run from startup and do not stop until the application exits, i.e. they're permanent. They run their own event loop and communicate with other threads via signals and slots. These are normal threading, not task-based concurrency.
Upvotes: 4
Views: 2710
Reputation: 8718
With the author of question explanation we can narrow down to exact type of threads:
I have predefined threads that run from startup and do not stop until the application exits, i.e. they're permanent. They run their own event loop and communicate with other threads via signals and slots. These are normal threading, not task-based concurrency. How can I organize this type of multi-threading?
It is quite easy to organize in Qt by "moving" the object with predefined signals and slots to the thread in which its methods supposed to run in.
class Worker : public QObject
{
Q_OBJECT
public:
Worker(Load*);
signals:
void produce(Result*);
public slots:
void doWork(Load*);
};
void Worker::doWork(Load* pLoad)
{
// here we can check if thread interruption is requested
// if the work is of iterative long time type
while(!QThread::currentThread()->isInterruptionRequested())
{
process(pLoad);
if (pLoad->finished())
{
emit produce(pLoad->result()); // deliver the result
return; // back to worker thread event loop to wait
}
}
// interrupted before the load finished
QThread::currentThread()->quit();
}
// { somewhere on the main thread
// main thread launches worker threads like this one
QThread thread;
Worker worker(new Load());
worker.moveToThread(&thread); // the worker will be leaving in the thread
// start!
thread.start();
// worker thread adds new workload on its thread
// through the thread event loop by invokeMethod
QMetaObject::invokeMethod(&worker, "doWork", Qt::AutoConnection,
Q_ARG(Load*, new Load));
// after all we want to quit the app
thread.requestInterruption(); // for faster finishing
// or
thread.quit(); // for finishing after the last data processed
thread.wait(); // for one thread wait but you can catch finished()
// signals from many threads (say, count until all signaled)
// ........
// quit the app
// ........
qApp->quit();
// } somewhere on main thread
It may look a bit like task-based concurrency but there is no task object on thread picking up its load from the queue. It just demonstrates Qt work thread communicating via signals and slots.
Upvotes: 1
Reputation: 8589
Most modern platforms will aim to 'clean up' after processes end abruptly. That is end all the threads, recover all the memory, close any open files and recover any other resources or handles allocated to a process.
But it not recommended that is relied on and when execution may have 'durable' side-effects such as writing to files or communicating with other processes (including sharing memory) that may survive termination it may not be possible to clean up easily. Files could remain half written and other processes may receive half complete messages or send messages to processes that didn't declare termination.
It's also very hard to identify memory leaks in processes that are ended abruptly.
Best practice is always going to bring all threads to a known conclusion.
The recommended way to terminate is to define one or more stop
flags (often bool
) that will be checked by threads at 'safe' points to terminate.
Those stop
flags should be atomic (std::atomic<>
) or protected by a std::mutex
if used in a wait()
condition.
In that model termination code could look something like..
#include <iostream>
#include <atomic>
#include <mutex>
#include <thread>
#include <vector>
std::atomic<bool> stop_flag;
std::vector<std::thread> threads;
std::mutex cout_mutex;//std::cout is not natively synchronized.
void chug(size_t index){
int i{0};
while(!stop_flag){
{
std::lock_guard<std::mutex> guard{cout_mutex};
std::cout<<index<<" : "<<i<<std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));//slow it down!
++i;
}
}
//stop_all brings all the threads to a safe and known conclusion.
void stop_all(){
stop_flag=true;
for( auto& curr: threads){
curr.join();
}
}
int main() {
const size_t num{10};
for(size_t i=0;i<num;++i){
threads.emplace_back(chug,i);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));//Let it run!
stop_all();
return 0;
}
Upvotes: 1
Reputation: 7146
It highly depends on how you use the threads.
If you use them as seperate eventloops, and not as "worker threads", simply stop the by quitting the threads from the QCoreApplication::aboutToQuit
signal:
QObject::connect(qApp, &QCoreApplication::aboutToQuit, thread, [thread](){
thread->quit();
thread->wait(1000);
});
(For multiple threads, first quit all of them and then wait)
In case you use them as real workerthreads, where you do permanent work in a loop etc, you can use QThreads interruptions mechanism. In your thread do:
while(!QThread::currentThread()->isInterruptionRequested()) {
// code...
}
and quit them in a very similar way:
QObject::connect(qApp, &QCoreApplication::aboutToQuit, thread, [thread](){
thread->requestInterruption();
thread->wait(1000);
});
Upvotes: 3
Reputation: 2497
I agree @UKMonkey idea for a thread block but if some threads are waiting for a device or a memory and condition waits that dose not guaranteed thread quit and even it's prevent application quit. so what to do for those situations , QCoreApplication has aboutToQuit() signal you can connect it to a slot and force your threads to quit and check if a thread don't quit gracefully and correct it's quit scenario.
Upvotes: 0
Reputation: 6993
Most long running 'thread main' functions have a form a bit like the following:
while (doWork) {
work();
}
with doWork being a std::atomic<bool>
.
When the main thread wants to quit, it sets myThread.doWork = false
on all the threads that are still alive, which allows them to fall out when they're ready.
By calling myThread.wait()
on the main thread, it blocks until the thread that you've told to stop doing work actually stops.
In doing this for all your threads, by the time the main thread leaves main() it's the only thread still running.
Side note: if you have to await work to be pushed to it, you probably want to look into the QWaitCondition class so that you can awake your thread both when there's work and when you want it to stop.
Upvotes: 4