由于 QCoreApplication 事件循环,QThread 永远不会退出
QThread never quits due to QCoreApplication event loop
问题
所以我有一个 CommandRetriever class 保存一些命令,应该 在不同的线程上执行这些命令。
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()
插槽是处理需要处理的内容。
出于某种原因,我的 rcon
对象的 doAsync()
函数将被调用,但线程永远不会退出...我的主要 class 看起来像这样:
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()
事件循环是无限的 运行,如果我是对的,我怎样才能正确关闭所有东西?
解决方案
正如暴徒所说:
You need to tell your QCoreApplication to quit, otherwise it will never return from the exec function.
这可以通过连接我的工作对象的 finished()
信号来让我的应用程序退出来轻松实现:
QObject::connect( rcon, SIGNAL( finished() ), app, SLOT( quit() ) );
但是,如果我有 window 或其他一些 GUI 元素,这样我就可以明确知道何时需要关闭我的程序(例如,用户关闭主 window).
因为我是 运行 多线程并且因为我的应用程序只是一个控制台应用程序,所以将线程的 finished()
信号连接到我的 QCoreApplication
是没有意义的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 );
}
}
需要注意的是 class RemoteConnection 扩展了 QRunnable
.
为了等待线程完成,我调用:
QThreadPool::globalInstance()->waitForDone();
这将阻塞主线程,直到所有线程完成执行,这对我的控制台应用程序更有意义。它还使代码变得更加清晰。
我还删除了 main.cpp 中的 app.exec()
,因为我不再需要 QCoreApplication
提供的事件循环。
我学到了什么? QThread
不是实现多线程的唯一方法。在我的例子中,使用 QThreadPool
更有意义,因为我不需要 QThread
的 signals/slots。此外,如果不需要,则不应使用 QCoreApplication
事件循环。大多数控制台应用程序都属于这一类。
来源
尝试将 RemoteConnection
的 finished()
信号连接到 QThread
的 terminate()
插槽而不是 quit()
插槽。
QObject::connect( rcon, SIGNAL( finished() ), thread, SLOT( terminate() ) );
线程可能会或可能不会立即终止,具体取决于操作系统的调度策略。在terminate()
之后使用QThread::wait()
,以确保。
如果您不告诉 QCoreApplication
对象退出,它将永远不会离开 exec()
函数。
您可以直接调用 QCoreApplication::quit()
插槽,也可以将信号连接到它。
...
connect(thread, SIGNAL(finished()), qApp, SLOT(quit()));
qApp
是引用唯一应用程序对象的全局指针。与调用 QCoreApplication::instance()
.
相同
只需在您的 dll class "CommandRetriever" 的初始化程序中创建一个新的 QCoreApplication 对象,然后调用:processEvents,一次。如下:
//Create the QCoreApplication object
int argc = 1;
char * argv[] = {"my.dll", NULL};
dllEventHandler = new QCoreApplication(argc, argv);
//Start: process events
dllEventHandler->processEvents();
就是这样。您不需要 main 函数或 QCoreApplication .exec().
问题
所以我有一个 CommandRetriever class 保存一些命令,应该 在不同的线程上执行这些命令。
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()
插槽是处理需要处理的内容。
出于某种原因,我的 rcon
对象的 doAsync()
函数将被调用,但线程永远不会退出...我的主要 class 看起来像这样:
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()
事件循环是无限的 运行,如果我是对的,我怎样才能正确关闭所有东西?
解决方案
正如暴徒所说:
You need to tell your QCoreApplication to quit, otherwise it will never return from the exec function.
这可以通过连接我的工作对象的 finished()
信号来让我的应用程序退出来轻松实现:
QObject::connect( rcon, SIGNAL( finished() ), app, SLOT( quit() ) );
但是,如果我有 window 或其他一些 GUI 元素,这样我就可以明确知道何时需要关闭我的程序(例如,用户关闭主 window).
因为我是 运行 多线程并且因为我的应用程序只是一个控制台应用程序,所以将线程的 finished()
信号连接到我的 QCoreApplication
是没有意义的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 );
}
}
需要注意的是 class RemoteConnection 扩展了 QRunnable
.
为了等待线程完成,我调用:
QThreadPool::globalInstance()->waitForDone();
这将阻塞主线程,直到所有线程完成执行,这对我的控制台应用程序更有意义。它还使代码变得更加清晰。
我还删除了 main.cpp 中的 app.exec()
,因为我不再需要 QCoreApplication
提供的事件循环。
我学到了什么? QThread
不是实现多线程的唯一方法。在我的例子中,使用 QThreadPool
更有意义,因为我不需要 QThread
的 signals/slots。此外,如果不需要,则不应使用 QCoreApplication
事件循环。大多数控制台应用程序都属于这一类。
来源
尝试将 RemoteConnection
的 finished()
信号连接到 QThread
的 terminate()
插槽而不是 quit()
插槽。
QObject::connect( rcon, SIGNAL( finished() ), thread, SLOT( terminate() ) );
线程可能会或可能不会立即终止,具体取决于操作系统的调度策略。在terminate()
之后使用QThread::wait()
,以确保。
如果您不告诉 QCoreApplication
对象退出,它将永远不会离开 exec()
函数。
您可以直接调用 QCoreApplication::quit()
插槽,也可以将信号连接到它。
...
connect(thread, SIGNAL(finished()), qApp, SLOT(quit()));
qApp
是引用唯一应用程序对象的全局指针。与调用 QCoreApplication::instance()
.
只需在您的 dll class "CommandRetriever" 的初始化程序中创建一个新的 QCoreApplication 对象,然后调用:processEvents,一次。如下:
//Create the QCoreApplication object
int argc = 1;
char * argv[] = {"my.dll", NULL};
dllEventHandler = new QCoreApplication(argc, argv);
//Start: process events
dllEventHandler->processEvents();
就是这样。您不需要 main 函数或 QCoreApplication .exec().