Problem
So I have a CommandRetriever class that holds some commands, and should execute these commands on different threads.
class CommandRetriever
{
public:
CommandRetriever();
~CommandRetriever();
void addCommand( QString, Command* );
void executeCommands();
private:
QMap<QString, Command*> m_commands;
};
addCommand
will add a new entry into m_commands
. Here is the function in question, however.
void CommandRetriever::executeCommands()
{
for( auto iter : m_commands.keys() )
{
Command *cmd = m_commands.value( iter );
// Command, password //
RemoteConnection *rcon = new RemoteConnection( cmd, "" );
QThread *thread = new QThread();
rcon->moveToThread( thread );
QObject::connect( thread, SIGNAL( started() ), rcon, SLOT( doAsync() ) );
QObject::connect( rcon, SIGNAL( finished() ), thread, SLOT( quit() ) );
QObject::connect( rcon, SIGNAL( finished() ), rcon, SLOT( deleteLater() ) );
QObject::connect( thread, SIGNAL( finished() ), thread, SLOT( deleteLater() ) );
thread->start();
}
}
RemoteConnection
is my worker thread. The doAsync()
slot is what processes what needs to be processed.
For some reason, my doAsync()
function for my rcon
object will be called, but the thread will never exit... my main class looks like this:
int main( int argc, char *argv[] )
{
QCoreApplication app( argc, argv );
CommandRetriever cr( addr, port );
//cr.addCommand() ...
cr.executeCommands();
return app.exec();
}
My worker objects (rcon
) will output what they're supposed to. However, even though my worker objects emit a finished()
signal, the thread still lives, and my application never finishes. I connected a signal like this before:
QObject::connect( rcon, SIGNAL( finished() ), thread, SLOT( deleteLater() ) );
But then I got the error: QThread: Destroyed while thread is still running
and a seg fault.
I recently posted a question that had a related issue to my threads, and the solution was to call thread->wait()
, however, I saw that if I have an event loop, thread->wait()
is not needed, and is not an appropriate solution. With the current code, I get no error, but my application runs forever. I'm guessing the app.exec()
event loop is infinitely running, and if I'm right, how can I shut everything down correctly?
Solution
As thuga said:
You need to tell your QCoreApplication to quit, otherwise it will never return from the exec function.
This could easily be achieved by connecting a my worker object's finished()
signal to make my app quit:
QObject::connect( rcon, SIGNAL( finished() ), app, SLOT( quit() ) );
However, this makes more sense if I had a window or some other GUI elements so that I would be able to definitively know when I need to shut down my program (e.g. the user closes the main window).
Because I was running multiple threads and because my application is only a console application, it didn't make sense to connect my thread's finished()
signal to my QCoreApplication
's quit()
slot. So what I ended up using was QThreadPool
:
void CommandRetriever::executeCommands()
{
// Quick iterate through the command map //
for( auto iter : m_commands.keys() )
{
Command *cmd = m_commands.value( iter );
// Command, password; start a new thread for a remote connection. //
RemoteConnection *rcon = new RemoteConnection( cmd, "" );
QThreadPool::globalInstance()->start( rcon );
}
}
It should be noted that the class RemoteConnection extends QRunnable
.
To wait for the threads to finish, I call:
QThreadPool::globalInstance()->waitForDone();
This will block the main thread until all of the threads finish execution, which makes more sense for my console application. It also makes the code a hell of a lot cleaner.
I also removed app.exec()
in my main.cpp because I no longer needed the event loop that QCoreApplication
provides.
What I learned? QThread
isn't the only way to achieve multithreading. In my case, it makes more sense to use QThreadPool
because I didn't need the signals/slots of QThread
. Also, you shouldn't use the QCoreApplication
event loop if it is not needed. Most console applications will fall into this category.
Sources
http://doc.qt.io/qt-5/qrunnable.html
http://doc.qt.io/qt-4.8/qthreadpool.html