Problem
所以我有一个 CommandRetriever 类来保存一些命令,并且should在不同的线程上执行这些命令。
class CommandRetriever
{
public:
CommandRetriever();
~CommandRetriever();
void addCommand( QString, Command* );
void executeCommands();
private:
QMap<QString, Command*> m_commands;
};
addCommand
将添加一个新条目m_commands
。然而,这是有问题的函数。
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
是我的工作线程。这doAsync()
slot 就是处理需要处理的内容。
由于某种原因,我的doAsync()
为我的功能rcon
对象将被调用,但线程永远不会退出...我的主类如下所示:
int main( int argc, char *argv[] )
{
QCoreApplication app( argc, argv );
CommandRetriever cr( addr, port );
//cr.addCommand() ...
cr.executeCommands();
return app.exec();
}
我的工人反对(rcon
)将输出他们应该输出的内容。然而,即使我的工作对象发出finished()
信号,线程仍然存在,并且我的应用程序永远不会完成。我之前连接过这样的信号:
QObject::connect( rcon, SIGNAL( finished() ), thread, SLOT( deleteLater() ) );
但后来我得到了错误:QThread: Destroyed while thread is still running
和段错误。
我最近发布了一个与我的线程相关的问题,解决方案是调用thread->wait()
,但是,我发现如果我有一个事件循环,thread->wait()
不需要,也不是合适的解决方案。使用当前代码,我没有收到任何错误,但我的应用程序会永远运行。我猜app.exec()
事件循环无限运行,如果我是对的,我怎样才能正确关闭一切?
Solution
正如暴徒所说:
您需要告诉 QCoreApplication 退出,否则它将永远不会从 exec 函数返回。
这可以通过连接我的工作对象来轻松实现finished()
让我的应用程序退出的信号:
QObject::connect( rcon, SIGNAL( finished() ), app, SLOT( quit() ) );
然而,如果我有一个窗口或其他一些 GUI 元素,这样我就能够明确地知道何时需要关闭程序(例如,用户关闭主窗口),这更有意义。
因为我正在运行多个线程,并且因为我的应用程序只是一个控制台应用程序,所以连接我的线程是没有意义的finished()
向我发出信号QCoreApplication
's quit()
投币口。所以我最终使用的是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 );
}
}
应该注意的是,RemoteConnection 类扩展了QRunnable
.
为了等待线程完成,我调用:
QThreadPool::globalInstance()->waitForDone();
这将阻塞主线程,直到所有线程完成执行,这对我的控制台应用程序更有意义。它还使代码变得更加干净。
我也删除了app.exec()
在我的 main.cpp 中,因为我不再需要事件循环QCoreApplication
提供。
我学到的是?QThread
并不是实现多线程的唯一方法。就我而言,使用更有意义QThreadPool
因为我不需要的信号/槽QThread
。另外,您不应该使用QCoreApplication
事件循环(如果不需要)。大多数控制台应用程序都属于这一类。
Sources
http://doc.qt.io/qt-5/qrunnable.html http://doc.qt.io/qt-5/qrunnable.html
http://doc.qt.io/qt-4.8/qthreadpool.html http://doc.qt.io/qt-4.8/qthreadpool.html